Skip to content

Instantly share code, notes, and snippets.

@rborn
Forked from kwhinnery/ti.js
Created July 7, 2012 10:16
Show Gist options
  • Save rborn/3065746 to your computer and use it in GitHub Desktop.
Save rborn/3065746 to your computer and use it in GitHub Desktop.
/*
* Mixin properties of n objects into the given object - pass in as many objects to mix in as you like.
* Can perform a shallow or deep copy (shallow is default for performance reasons).
*
* Usage:
* mixin([Boolean deepCopy,] objectToExtend, objectToMixIn [, as many other objects as needed])
*
* Examples:
*
* var o1 = {
* foo:'bar',
* anObject: {
* some:'value'
* clobber:true
* }
* };
*
* var o2 = {
* foo:'bazooka',
* aNewProperty:'something',
* anObject: {
* some:'other value'
* }
* };
*
* //merge properties of o2 into o1
* mixin(o1,o2);
* alert(o1.foo); //gives 'bazooka'
* alert(o1.anObject.clobber); //exception - clobber was blown away on the shallow copy
*
* //recursively merge objects
* mixin(true,o1,02);
* alert(o1.anObject.some); //gives 'other value'
* alert(o1.anObject.clobber); //gives true, since the merge was recursive
*
* //clone o1:
* var clone = mixin({},o1);
* alert(clone.foo); //gives 'bar'
*
*/
//helper function for mixin - adapted from jQuery core
function _isPlainObject(obj) {
if(!obj || typeof obj !== 'object' || obj.nodeType) {
return false;
}
try {
// Not own constructor property must be Object
if(obj.constructor && !Object.prototype.hasOwnProperty.call(obj, "constructor") && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
} catch ( e ) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for(key in obj ) {}
return key === undefined || Object.prototype.hasOwnProperty.call(obj, key);
}
//helper for main mixin function interface, defined below this function as "mixin"
function _merge(obj1, obj2, recursive) {
for(var p in obj2) {
if(obj2.hasOwnProperty(p)) {
try {
if(recursive && _isPlainObject(obj2[p])) {
obj1[p] = _merge(obj1[p], obj2[p]);
}
else {
obj1[p] = obj2[p];
}
}
catch(e) {
obj1[p] = obj2[p];
}
}
}
return obj1;
}
//main interface, exposed in module
function mixin() {
var obj, mixins, deep = false;
if (typeof arguments[0] === 'boolean') {
deep = arguments[0];
obj = arguments[1];
mixins = Array.prototype.slice.call(arguments,2);
}
else {
obj = arguments[0];
mixins = Array.prototype.slice.call(arguments,1);
}
//mix in properties of given objects
for (var i = 0, l = mixins.length; i<l; i++) {
_merge(obj,mixins[i],deep);
}
return obj;
}
exports.mixin = mixin;
/*
* Platform helper functionality to allow you to branch values or
* logic based on platform.
*
* Examples:
*
*
*
*/
var osname = Ti.Platform.osname,
version = parseInt(Ti.Platform.version),
is = {}; //our public interface
//Set up boolean variables for the various checks we want to do
is.android = osname === 'android';
is.ios = osname === 'iphone' || osname === 'ipad';
//add to public API
exports.osname = osname;
exports.version = version;
exports.is = is;
/*
* Wrapper for Titanium UI components. This wrapper provides a few pieces of critical
* functionality, currently missing from Titanium UI objects:
* - The ability to safely extend components with new members
* - Resource management and object lifecycle handling
* - Some syntactical sugar
*
* Caveat Number One:
* Not all Titanium UI objects are perfectly wrappable. Some still need to be used directly,
* notably TableViews and TableViewRows, since they contain LOTS of magic - like purple, sparkly
* unicorn magic. If you need a TableView, it's best to have it as a child of a Component-ized
* view, and work with the TableView directly.
*
* Caveat Number Two:
* This is not an Appcelerator-supported API - this is one approach to accomplishing the goals of
* best-practice UI development. This Component wrapper can be considered an advanced use of the
* Titanium API, and should only be used by developers who are already knowledgeable about the core
* Titanium JavaScript APIs.
*
*/
function Component(/*Titanium Proxy Object*/ tiView) {
this.viewProxy = tiView;
this.globalHandlers = {};
}
//Wrappers for common Titanium view construction functions
Component.prototype.add = function(tiChildView) {
var v = tiChildView.viewProxy||tiChildView;
this.viewProxy.add(v);
};
Component.prototype.remove = function(tiChildView) {
var v = tiChildView.viewProxy||tiChildView;
this.viewProxy.remove(v);
};
Component.prototype.open = function(args) {
if (this.viewProxy.open) {
this.viewProxy.open(args||{animated:false});
}
};
Component.prototype.close = function(args) {
if (this.viewProxy.close) {
this.viewProxy.close(args||{animated:false});
}
};
Component.prototype.animate = function(args,callback) {
this.viewProxy.animate(args,callback||function(){});
};
//Getter/Setter for the wrapped Titanium view proxy object
Component.prototype.get = function(key) {
return this.viewProxy[key];
};
Component.prototype.set = function(key,value) {
this.viewProxy[key] = value;
};
//Event Handling
Component.prototype.on = function(event,callback) {
switch (event) {
case 'location':
this.globalHandlers.location = callback;
Ti.Geolocation.addEventListener('location', this.globalHandlers.location);
break;
case 'orientationchange':
this.globalHandlers.orientationchange = callback;
Ti.Gesture.addEventListener('orientationchange', this.globalHandlers.orientationchange);
break;
default:
this.viewProxy.addEventListener(event,callback);
break;
}
};
Component.prototype.fire = function(event,data) {
this.viewProxy.fireEvent(event,data||{});
};
//Sugar for handling orientation change
Component.prototype.orientation = function(branches) {
this.on('orientationchange', function(e) {
switch (e.orientation) {
case Ti.UI.PORTRAIT:
case Ti.UI.UPSIDE_PORTRAIT:
if (branches.portrait) {
branches.portrait.call(this);
}
break;
case Ti.UI.LANDSCAPE_LEFT:
case Ti.UI.LANDSCAPE_RIGHT:
if (branches.landscape) {
branches.landscape.call(this);
}
break;
}
});
};
//This should be overridden by any Components which wish to execute custom
//clean up logic, to release their child components, etc.
Component.prototype.onDestroy = function() {};
//Clean up resources used by this Component
Component.prototype.release = function() {
//remove global event handlers
if (this.globalHandlers.location) {
Ti.Geolocation.removeEventListener('location', this.globalHandlers.location);
}
if (this.globalHandlers.orientationchange) {
Ti.Gesture.removeEventListener('orientationchange', this.globalHandlers.orientationchange);
}
//force cleanup on proxy
this.viewProxy = null;
//run custom cleanup logic
this.onDestroy();
};
//adding to public interface
exports.Component = Component;
//Component-wrapped and sugared Titanium UI object constructors - most common ones included, add/remove as needed
exports.Window = function(args) {
return new Component(Ti.UI.createWindow(args));
};
exports.View = function(args) {
return new Component(Ti.UI.createView(args));
};
exports.Button = function(args) {
return new Component(Ti.UI.createButton(args));
};
exports.ScrollView = function(args) {
return new Component(Ti.UI.createScrollView(args));
};
exports.ScrollableView = function(args) {
var views = [],
newargs = args;
//unwrap child views of ScrollableView
for(var i = 0, l = _args.views.length; i<l; i++) {
var v = _args.views[i].viewProxy||_args.views[i];
views.push(v);
}
newargs.views = views;
return new Component(Ti.UI.createScrollableView(newargs));
};
exports.ImageView = function(image,args) {
var newargs = args||{};
newargs.image = image;
return new Component(Ti.UI.createImageView(newargs));
};
exports.Label = function(textOrKey, args) {
var newargs = args||{};
newargs.text = L(textOrKey,textOrKey);
return new Component(Ti.UI.createLabel(newargs));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment