Comparing version 0.3.3 to 1.0.1
{ | ||
"name": "staballoy", | ||
"description": "Reactive Library for binding data to UI elements in Titanium Alloy", | ||
"version": "0.3.3", | ||
"version": "1.0.1", | ||
"main": "staballoy.js", | ||
@@ -15,3 +15,7 @@ "keywords": ["titanium","reactive","titanium-module"], | ||
}, | ||
"homepage": "http://renepot.net" | ||
"homepage": "http://renepot.net", | ||
"dependencies": { | ||
"deepmerge": "^4.2.2", | ||
"is-mergeable-object": "^1.1.1" | ||
} | ||
} |
228
README.md
@@ -10,218 +10,104 @@ # Staballoy | ||
| :warning: Breaking changes :warning: | | ||
|------------------------| | ||
| Version 1.0.0 introduced massive breaking changes as opposed to any 0.x version. Staballoy also no longer overrides the `Alloy.createController` method and therefore is completely compatible with any other framework. | | ||
## How does it work? | ||
Staballoy injects itself in every controller and can handle monitoring of variables for you in a reactive way. You can subscribe any UI component in staballoy and your UI component will be updated for the argument you provide. | ||
Staballoy maintains an object of data, stored in `Ti.App.Properties`, any alteration in this data (by setting new properties) will cause the library to find any UI elements that need updating automatically. | ||
## Setup | ||
So enough talk, how do I install it? First download the latest release (from the dist folder) and install it as a commonjs module. | ||
*Alpha setup: copy staballoy.js to your lib directory, and then install the dependencies listed in `package.json` manually* | ||
<modules> | ||
<module platform="commonjs" version="0.1">staballoy</module> | ||
</modules> | ||
### Through NPM | ||
In classic go to the `Resources` folder, for **Alloy** go to the `lib` directory of your project, then run the following command | ||
So enough talk, how do I install it? For **Alloy** projects go to the `lib` directory of your project, then run the following command `npm i staballoy`. | ||
`npm install staballoy` | ||
_Note: if your app doesn't have `node_modules` yet, run `npm init` first!_ | ||
### Include it in your application | ||
## Getting/Setting data | ||
Next, require staballoy in your `alloy.js` file. | ||
Staballoy stores all data in a single `Ti.App.Properties` object, so anything you set will add to this dataset. Staballoy automatically deep-merges the data you set, so you do not have to worry about overwriting/losing data if you do not include the already set data. | ||
var staballoy = require('staballoy'); | ||
### Setting data | ||
To set data in staballoy, all you need to do is call the `set` method and provide any data you want to **add** to the dataset | ||
Now, this will override `Alloy.createController` so it is not compatible with any other modules/libraries that do the same. Keep this in mind! (barely any modules/library does this, but [Jason Kneens AlloyXL](https://github.com/jasonkneen/AlloyXL) is an example) | ||
```js | ||
require('staballoy').set(object); | ||
``` | ||
Next, staballoy _**requires**_ a newer underscorejs lib to function, you can do so by downloading the [latest underscorejs](http://underscorejs.org) and putting that in `/lib/alloy/underscore.js`. You can follow [this](https://jira.appcelerator.org/browse/ALOY-1583) ticket on JIRA to monitor when that is done by default. | ||
An automatic deep merge will be initiated on the data you set. | ||
## Supported controllers | ||
### Getting data | ||
To get all the data currently set in the staballoy storage, call the `get` method | ||
Every Window controller will have access to a `$.args.subscribe` method as well as `$.args.staballoy` which contains methods to set and get variables stored. The current supported controller types are | ||
- Ti.UI.Window | ||
- Ti.UI.TabGroup | ||
- Ti.UI.iOS.NavigationWindow | ||
Staballoy automatically removes subscriptions based on the `close` event of these UI elements. If it wouldn't be able to do this there would be memory leaks. It is important to only use Windows that are created by alloy! | ||
## Variables | ||
You can set variables stored in staballoy. Any variable set are single session. There is no storage. Therefore staballoy should not be used as storage. Keep that in mind when using it. | ||
You can `set` and `get` variables in any Alloy created controller, as it will have the following methods exposed: | ||
$.args.staballoy.setVar('myVarName', 'value'); | ||
var value = $.args.staballoy.getVar('myVarName'); | ||
Thats all you need to do to update variables. Again, if you want to store the data you should handle the storage of it, and init the right data when starting the app, in `alloy.js` for example, or better in a library file (eg. `/lib/initStaballoy.js`) called from `alloy.js` | ||
### Deep setting/getting | ||
If you're storing objects in variables, you can update them without having to get them first. For example, if you have this object: | ||
```js | ||
var config = { | ||
"user": { | ||
"preferences": { | ||
"notifications": true, | ||
"language": "en" | ||
} | ||
} | ||
} | ||
staballoy.setVar('config', config); | ||
const data = require('staballoy').get(); | ||
``` | ||
If you now want to update the language, you can do it like this: | ||
```js | ||
staballoy.setVar('config_user_preferences_language', 'de'); | ||
``` | ||
The `get` method has an optional argument, if you provide this argument you can return a subset of the data. A deep search is performed to get this property, so you can provide a path to the property you want to fetch. | ||
Same with getting it. | ||
```js | ||
staballoy.getVar('config_user_preferences_language'); | ||
// results in "de" | ||
const data = require('staballoy').get('my.nested.property'); | ||
``` | ||
You can even set properties on object trees that don't exist yet, and it will make the tree for you | ||
```js | ||
staballoy.setVar('config_user_info_name', 'myName'); | ||
``` | ||
This will return `Hello, World!` when the data has been set as `{my: { nested: { property: "Hello, World!"}}}`. | ||
## Subscribe | ||
Any of the supported controllers will be automatically parsed and checked for subscriptions. These subscriptions can be configured in the tss file. Say we have a label | ||
<Label id="myLabel" /> | ||
And you want to subscribe the text property to the variable `closeButtonName`. You can do that by putting the following in tss | ||
``` | ||
"#myLabel": { | ||
staballoy: { | ||
subscriptions: { | ||
"closeButtonName": "text" | ||
} | ||
} | ||
} | ||
``` | ||
Of course the most important feature is subscribing to any data you have set. To enable subscribing all you need to do is add the `module="staballoy"` tag to any UI element you want to have support for this. | ||
The subscriptions object specifies what to subscribe to and to what attribute. In this example I am subscribing to the `closeButtonName` variable, and I want the `text` attribute to be set with the value that is contained in the variable. Multiple subscriptions are possible for every element. | ||
Internally, every time the `closeButtonName` variable is updated, the following code is executed: `$.myLabel.text = closeButtonName;` | ||
```xml | ||
<View module="staballoy" id="myView" /> | ||
``` | ||
As with `setVar` and `getVar` you can also use deep-binding and it will get it from inside the object | ||
``` | ||
"#myLabel": { | ||
staballoy: { | ||
subscriptions: { | ||
"config_user_info_name": "text" | ||
} | ||
} | ||
} | ||
``` | ||
To enable an entire controller at once, add this to the Alloy tag. | ||
## 2-way data-binding | ||
When you subscribe to a property with the value binding, it will automatically be 2-way. The tss sample below is of a textfield. | ||
```xml | ||
<Alloy module="staballoy"> | ||
``` | ||
staballoy: { | ||
subscriptions: { | ||
"user_name": "value" | ||
} | ||
}, | ||
``` | ||
So when the value of the textfield changes, using the `change` event, the `user_name` variable will be updated automatically as well. So when you bind it to a label somewhere else, that will update automatically too. | ||
## Manual Subscribe | ||
If you don't want to subscribe in the tss but want to do it more dynamically, in the controller, you can do it as follows: | ||
Once this is set, dive into your `tss` and add subscriptions, using a path to the property you want to set it to, and the property you want to update. | ||
$.args.subscribe({ | ||
'component' : $.myLabel, | ||
'window' : $, | ||
'subscriptions' : {'closeButtonName': 'text'}, | ||
}); | ||
### Example 1 | ||
You want to set the color and text property of a `<Label>` based on a user profile: | ||
The object consists of: | ||
```xml | ||
<Label id="myLabel" module="staballoy" /> | ||
``` | ||
- **component** - The UI element you want updated | ||
- **window** - The window the UI element is in, this needs to be an alloy generated window, so usually `$` is enough. The window GUID is also accepted | ||
- **subscriptions** - As explained above. | ||
As you can see this flow is the same as the earlier one, but it is a little more complex as it needs to know the context. | ||
### Using setters | ||
Instead of using an attribute, you can also use the setters. So if you provide the following for subscriptions: | ||
'subscriptions' : {'closeButtonName': 'setText'}, | ||
This will internally be translated to: `$.myLabel.setText(closeButtonName)` | ||
## Manipulating the data that is being set | ||
So want to alter the data of the variable before it being set you can use the `logicFunction`. It can be really powerfull if you store objects in staballoy. You can do that as following: | ||
$.args.staballoy.setVar('closeButtonStuff', {"id" : 5, "key": "buttonTitle"}); | ||
You then set the logicFunction has a string. This is of course a property in the tss, but can also be specified in the manual flow | ||
logicFunction: "myLogicFunction" | ||
Then, you can add the logicFunction to the `$` nameSpace in the controller | ||
$.myLogicFunction = function(value){ | ||
return L(value.key); | ||
}; | ||
In the manual approach you can, instead of a string to the `$` namespace, you can also add in the function directly, so it can also be an anonymous function | ||
logicFunction: function(value){ | ||
return L(value.key); | ||
Then in the `tss`: | ||
```js | ||
"#myLabel": { | ||
staballoy: { | ||
text: "user.name", | ||
color: "user.favoriteColor" | ||
} | ||
Now, you can use part of an object to set the value of your UI component automatically. Another example for visibilty of an object based on an integer you can find in the sample app. | ||
## Subscribe in other controllers | ||
} | ||
``` | ||
The preferred method would be to pass the window GUID to the child controller, so you can pass the guid in the manual subscribe method inside a sub-controller | ||
Then setting that data in staballoy is simple: | ||
// index.js | ||
Alloy.createController('mySubController', {parentGuid: $.args.staballoy.guid}); | ||
// mySubController | ||
$.args.subscribe({ | ||
window: $.args.parentGuid | ||
}); | ||
```js | ||
require('staballoy').set({user: {name: "John Doe", color: "#6F2E25"}}); | ||
``` | ||
In case you create the controller inside an XML, you should create a setter and start subscribing after the setter has been called. | ||
This will automatically change the color and text of the Label, on the fly. | ||
You could also use an exported function in the sub-controller, and use the main controller (a window) to subscribe. An example of this is included in the sample app. | ||
## Debug | ||
Want to get more logging? Enable debug mode! Add this line to `alloy.js` and you should be good | ||
```js | ||
require('staballoy').setDebug(true); | ||
``` | ||
// index.js | ||
$.args.subscribe({ | ||
'component' : $.subView, | ||
'window' : $, | ||
'subscriptions' : {'closeButtonName': 'setVisible'} | ||
}); | ||
`$.subView` is a `<Require>` in `index.xml`. | ||
<Require src="subView" id="subView"></Require> | ||
As I'm using the `setVisible` property, I can have an exported function in `subView.js` so that can be called. | ||
exports.setVisible = function(value){ | ||
$.getView().visible = value; | ||
}; | ||
> This flow is not really optimal so expect it to change in the future with a non-backwards compatible implementation. Suggestions for the new flow are welcome. | ||
**Any UI property that is not creation- or read-only is supported. Most UI elements are supported too** | ||
# Missing features / bugs / questions? | ||
For missing features and bugs, please submit a ticket, or even better submit a Pull Request. | ||
If you have any questions, just contact me on [TiSlack](http://tislack.org). My name there is @Wraldpyk. There also is a `#staballoy` channel. | ||
If you have any questions, just contact me on [TiSlack](http://tislack.org). My name there is @Wraldpyk. | ||
Want to support my work? Send a donation my way via the PayPal button on [TiSlack.org](http://tislack.org). Any support is appreciated. | ||
Want to support my work? Send a donation my way via the Ko-Fi button on [TiSlack.org](http://tislack.org). Any support is appreciated. | ||
## Changelog | ||
- **1.0.1** - (20210127) Added garbage-collection and debug mode | ||
- **1.0.0** - (20210119) Complete rewrite of the module | ||
- **0.3.3** - (20180905) Fixed issue where creating empty controllers crashed the app | ||
@@ -228,0 +114,0 @@ - **0.3.0** - (20180711) Added 2-way binding and deep-binding. |
354
staballoy.js
/** | ||
* Staballoy is created by Rene Pot (2017) | ||
* Version 0.3.3 -- 2018-09-05 | ||
* It extends alloy to add reactive components to Titanium. | ||
* Staballoy is created by Rene Pot (2021) | ||
* Version 1.0.1 -- 2021-01-28 | ||
* The latest version can be found at Github: https://github.com/topener/staballoy | ||
* Or npmjs: https://www.npmjs.com/package/staballoy | ||
*/ | ||
var requiredControllers = {}; | ||
var activeControllers = []; | ||
var subscribers = []; | ||
var vars = {}; | ||
var currentlyFocussed = false; | ||
let data = Ti.App.Properties.getObject('Staballoy.DataSub', {}); | ||
let subscriptions = {}; | ||
let debug = false; | ||
const deepmerge = require('deepmerge'); | ||
/** | ||
* Alloy.createController override | ||
*/ | ||
Alloy.createController = function(name, args) { | ||
var args = args || {}; | ||
if (Alloy.CFG.debug) console.log('creating controller in staballoy: ', name, args); | ||
if (!requiredControllers[name]){ | ||
requiredControllers[name] = require("/alloy/controllers/" + name); | ||
} | ||
var controllerGuid = guid(); | ||
args.staballoy = {setVar: setVar, getVar: getVar, guid: controllerGuid}; | ||
args.subscribe = subscribe; | ||
var controller = new (requiredControllers[name])(args); | ||
// ignoring this controller as it has no xml elements | ||
if (!controller.getView()) return controller; | ||
controller.getView().guid = controllerGuid; | ||
// only parse through all subscribable components | ||
if (isSubscribable(controller.getView().apiName)){ | ||
if (controller.getView().apiName == 'Ti.UI.iOS.NavigationWindow'){ | ||
parseChildren([controller.getView().window], controller); | ||
} else { | ||
parseChildren(controller.getView().children, controller); | ||
} | ||
if (controller.getView().staballoy){ | ||
parseSubscriptions(controller.getView(), controller); | ||
} | ||
activeControllers.push({controller: controller, guid: controllerGuid}); | ||
controller.getView().addEventListener('focus', handleFocus); | ||
controller.getView().addEventListener('close', handleClose); | ||
if (!currentlyFocussed){ | ||
currentlyFocussed = controllerGuid; | ||
} | ||
} | ||
return controller; | ||
}; | ||
/** | ||
* look through all UI elements and find those that need subscribing | ||
*/ | ||
function parseChildren(children, controller){ | ||
_.each(children, function(child){ | ||
if (child.children && child.children.length > 0){ | ||
parseChildren(child.children, controller); | ||
if (child.staballoy){ | ||
parseSubscriptions(child, controller); | ||
} | ||
} else if (child.sections && child.sections.length > 0){ | ||
parseChildren(child.sections, controller); | ||
} | ||
if (child.staballoy){ | ||
parseSubscriptions(child, controller); | ||
} | ||
function handleChange() { | ||
let changes = 0; | ||
Object.keys(subscriptions).forEach(sub => { | ||
changes += parseSubscriptions(sub.UI); | ||
}); | ||
log(`${changes} changes in subscriptions`); | ||
} | ||
/** | ||
* parse through all subscriptions of the component | ||
*/ | ||
function parseSubscriptions(elem, controller){ | ||
if (!elem.staballoy.subscriptions || elem.staballoy.subscriptions.length == 0) return; | ||
// subscriptions have already been created. Don't do it again | ||
if (elem.staballoy.guid) return; | ||
_.each(elem.staballoy.subscriptions, function(attribute, variable){ | ||
var subscriberId = elem.staballoy.guid || guid(); | ||
// create a subscription model | ||
var data = {"var": variable, "component": elem, "attribute": attribute, "window": controller.args.staballoy.guid, guid: subscriberId}; | ||
elem.staballoy.guid = subscriberId; | ||
if (elem.staballoy.logicFunction){ | ||
if (typeof elem.staballoy.logicFunction == 'string'){ | ||
if (controller[elem.staballoy.logicFunction] && typeof controller[elem.staballoy.logicFunction] == 'function'){ | ||
data.logicFunction = controller[elem.staballoy.logicFunction]; | ||
} | ||
} else { | ||
data.logicFunction = elem.staballoy.logicFunction; | ||
} | ||
} | ||
if (attribute === 'value'){ | ||
elem.addEventListener('change', valueChange); | ||
data.valueListener = true; | ||
} | ||
trigger(data); | ||
subscribers.push(data); | ||
}); | ||
} | ||
function createSubscription(type, args) { | ||
let UI = Ti.UI[type](args); | ||
if (!args.hasOwnProperty('staballoy')) return UI; | ||
log('creating new subscription'); | ||
UI.addEventListener('postlayout', PLHandler); | ||
UI.staballoyGuid = guid(); | ||
let sub = {UI: UI, subscription: args.staballoy}; | ||
parseSubscriptions(UI); | ||
subscriptions[UI.staballoyGuid] = sub; | ||
return UI; | ||
} | ||
function valueChange(e) { | ||
if (e.source && e.source.staballoy) { | ||
_.each(e.source.staballoy.subscriptions, function(attribute, variable) { | ||
if (attribute === 'value') { | ||
setVar(variable, e.source.value, e.source.staballoy.guid); | ||
} | ||
}); | ||
function PLHandler(e) { | ||
// sanity check | ||
if (!e.source.hasOwnProperty('staballoyGuid')) return false; | ||
let parentWindow = findParentWindow(e.source); | ||
if (parentWindow) { | ||
subscriptions[e.source.staballoyGuid].parentWindow = parentWindow; | ||
subscriptions[e.source.staballoyGuid].UI.removeEventListener('postlayout', PLHandler); | ||
} | ||
} | ||
/** | ||
* The in-controller exposed subscribe method | ||
*/ | ||
function subscribe(args){ | ||
if (typeof args.window == 'string' && getWindowIndexById(args.window) > -1){ | ||
args.window = activeControllers[getWindowIndexById(args.window)].controller; | ||
function findParentWindow(el) { | ||
if (el.apiName !== 'Ti.UI.Window' && el.parent) return findParentWindow(el.parent); | ||
if (el.apiName === 'Ti.UI.Window' && el.hasOwnProperty('staballoyGuid')) return el.staballoyGuid; | ||
if (el.apiName === 'Ti.UI.Window' && !el.hasOwnProperty('staballoyGuid')) { | ||
el.staballoyGuid = guid(); | ||
el.addEventListener('close', handleWindowClose); | ||
log(`Found new window ${el.staballoyGuid}. Added monitoring`); | ||
return el.staballoyGuid; | ||
} | ||
if (!isSubscribable(args.window.getView().apiName)){ | ||
return console.error('subscription not possible without context window'); | ||
} | ||
if (!args.subscriptions || args.subscriptions.length == 0) return; | ||
args.component.staballoy = {subscriptions: args.subscriptions, logicFunction: args.logicFunction}; | ||
parseSubscriptions(args.component, args.window); | ||
return false; | ||
} | ||
/** | ||
* handle focus and store which is focussed last | ||
*/ | ||
function handleFocus(e){ | ||
currentlyFocussed = e.source.guid; | ||
} | ||
function getWindowIndexById(guid){ | ||
return _.findIndex(activeControllers, function(c){ | ||
return c.guid == guid; | ||
function handleWindowClose(e) { | ||
e.source.removeEventListener('close', handleWindowClose); | ||
if (!e.source.staballoyGuid) return; | ||
let changes = 0; | ||
Object.keys(subscriptions).forEach(key => { | ||
if (subscriptions[key].hasOwnProperty('parentWindow') && subscriptions[key].parentWindow === e.source.staballoyGuid) { | ||
// remove just in case | ||
subscriptions[key].UI.removeEventListener('postlayout', PLHandler); | ||
// remove subscription entirely | ||
delete subscriptions[key]; | ||
changes++; | ||
} | ||
}); | ||
} | ||
/** | ||
* Callback method on the close event listener | ||
*/ | ||
function handleClose(e){ | ||
var index = getWindowIndexById(e.source.guid); | ||
var controller = activeControllers[index].controller; | ||
controller.getView().removeEventListener('close', handleClose); | ||
controller.off(); | ||
controller.destroy(); | ||
activeControllers.splice(index,1); | ||
removeSubscribersForWindow(e.source.guid); | ||
log(`cleaned up ${changes} subscriptions because of closing window ${e.source.staballoyGuid}`); | ||
} | ||
/** | ||
* remove all subscribers based on the GUID of a window | ||
*/ | ||
function removeSubscribersForWindow(guid) { | ||
var toRemove = _.where(subscribers, { | ||
'window' : guid | ||
}); | ||
_.each(toRemove, function(r) { | ||
var removed = subscribers.splice(subscribers.indexOf(r), 1); | ||
if (removed && removed[0] && removed[0].valueListener) { | ||
removed[0].component.removeEventListener('value', valueChange); | ||
function parseSubscriptions(UI) { | ||
let changes = 0; | ||
if (!UI || !UI.staballoy) return 0; | ||
Object.keys(UI.staballoy).forEach((key) => { | ||
let res = find(data, UI.staballoy[key]); | ||
if (res) { | ||
if (key.indexOf('set') === 0) { | ||
UI[key](res); | ||
} | ||
else if (UI[key] !== res) { | ||
UI[key] = res; | ||
changes++; | ||
} | ||
} | ||
}); | ||
return changes; | ||
} | ||
/** | ||
* Set variable in staballoy and trigger update throughout all subscribers | ||
*/ | ||
function setVar(key, value, sourceguid) { | ||
function setVal(path, val) { | ||
var schema = vars; | ||
var pList = path.split('_'); | ||
var len = pList.length; | ||
for (var i = 0; i < len - 1; i++) { | ||
var elem = pList[i]; | ||
if (!schema[elem]) | ||
schema[elem] = {}; | ||
schema = schema[elem]; | ||
function find(obj, path) { | ||
try { | ||
path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties | ||
path = path.replace(/^\./, ''); // strip a leading dot | ||
var a = path.split('.'); | ||
for (var i = 0, n = a.length; i < n; ++i) { | ||
var k = a[i]; | ||
if (k in obj) { | ||
obj = obj[k]; | ||
} else { | ||
return; | ||
} | ||
} | ||
if (_.isEqual(getVar(path), value)) | ||
return false; | ||
schema[pList[len - 1]] = val; | ||
return true; | ||
return obj; | ||
} catch (e) { | ||
log(`Subscription path ${path} not found`) | ||
return; | ||
} | ||
} | ||
if (!setVal(key, value)) { | ||
return false; | ||
} | ||
const supportedUI = [" createActivityIndicator", "createAlertDialog", "createAttributedString", "createButton", "createButtonBar", "createImageView", "createLabel", "createListView", "createMaskedImage", "createNavigationWindow", "createOptionDialog", "createPicker", "createPickerColumn", "createPickerRow", "createProgressBar", "createRefreshControl", "createScrollableView", "createScrollView", "createSearchBar", "createSlider", "createSwitch", "createTab", "createTabbedBar", "createTabGroup", "createTableView", "createTableViewRow", "createTableViewSection", "createTextArea", "createTextField", "createToolbar", "createView", "createWebView", "createWindow"]; | ||
supportedUI.forEach(el => { | ||
exports[el] = function(args) { return createSubscription(el, args); } | ||
}); | ||
var options = []; | ||
var parts = key.split('_'); | ||
var string = parts[0]; | ||
_.each(parts, function(p, i){ | ||
if (i > 0) string += '_' + p; | ||
options.push(string); | ||
}); | ||
var toUpdate = _.filter(subscribers, function(sub){ | ||
if (sub.var.indexOf(key + '_') === 0) return true; | ||
if (options.indexOf(sub.var) > -1) return true; | ||
return false; | ||
}); | ||
_.each(toUpdate, function(sub) { | ||
// only update if the origin of the change isn't itself | ||
if (!sourceguid || sourceguid !== sub.guid) | ||
trigger(sub); | ||
}); | ||
exports.set = function(newData) { | ||
data = deepmerge(data, newData); | ||
Ti.App.Properties.setObject('Staballoy.DataSub', data); | ||
handleChange(); | ||
} | ||
/** | ||
* Trigger a variable update | ||
*/ | ||
function trigger(sub){ | ||
var value = getVar(sub.var); | ||
if (sub.logicFunction){ | ||
value = sub.logicFunction(value, sub); | ||
} | ||
if (sub.attribute.indexOf('set') === 0){ | ||
return sub.component[sub.attribute](value); | ||
} | ||
sub.component[sub.attribute] = value; | ||
exports.reset = function(newData) { | ||
data = newData; | ||
Ti.App.Properties.setObject('Staballoy.DataSub', data); | ||
handleChange(); | ||
} | ||
/** | ||
* Get variable from staballoy | ||
*/ | ||
exports.get = function(prop) { | ||
if (!prop) return data; | ||
return find(data, prop); | ||
}; | ||
function getVar(key) { | ||
function getVal(path) { | ||
var schema = vars; | ||
var pList = path.split('_'); | ||
var len = pList.length; | ||
for (var i = 0; i < len - 1; i++) { | ||
var elem = pList[i]; | ||
if (!schema[elem]) | ||
return false; | ||
schema = schema[elem]; | ||
} | ||
return _.clone(schema[pList[len - 1]]); | ||
} | ||
return getVal(key); | ||
exports.setDebug = function(state) { | ||
debug = typeof state === 'boolean' ? state : false; | ||
} | ||
// helpers | ||
/** | ||
* Check if, based on ApiName, a subscription is allowed | ||
*/ | ||
function isSubscribable(apiName){ | ||
return apiName === 'Ti.UI.Window' || apiName === 'Ti.UI.iOS.NavigationWindow' || apiName === 'Ti.UI.TabGroup'; | ||
} | ||
/** | ||
* Method to get a unique GUID (from Alloy source code) | ||
@@ -292,5 +149,4 @@ */ | ||
module.exports = { | ||
setVar: setVar, | ||
getVar: getVar | ||
}; | ||
function log(value) { | ||
if (debug) console.log('[staballoy]', value); | ||
} |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
0
11846
2
134
120
1
+ Addeddeepmerge@^4.2.2
+ Addedis-mergeable-object@^1.1.1
+ Addeddeepmerge@4.3.1(transitive)
+ Addedis-mergeable-object@1.1.1(transitive)