Skip to content

Instantly share code, notes, and snippets.

@egomez99
Created August 2, 2012 15:46
Show Gist options
  • Save egomez99/3238054 to your computer and use it in GitHub Desktop.
Save egomez99/3238054 to your computer and use it in GitHub Desktop.
[ERROR] Script Error = 'undefined' is not an object (evaluating 'this._cp.tabs') at tabgroup.js (line 275).
/**
* Open the login window for user to get access key!
* Once user has access key, will be redirect to the Active tab
*
* @param tabGroup
*/
function showLoginWindow()
{
purgeAll();
OF.mobile.UI.TabGroup.closeAllWindows();
if(OF.isIOS()){
(OF.mobile.UI.createWindow({
tabBarHidden : true,
_url : 'login_window.js',
transition : false,
navBarHidden : true,
addToControl : false
})).ofOpen();
}
else{
var login = OF.mobile.UI.createSimpleView({
tabBarHidden : true,
_url : 'login_window.js',
transition : false,
navBarHidden : true,
addToControl : false
});
if(!OF.mobile.UI.create_login_window){
OF.include('login_window.js');
}
OF.mobile.UI.create_login_window(login);
OF.mobile.UI.TabGroup.getComponent().getMainWindow().add(login);
login.show();
}
}
function runApplication(){
if(OF.isIOS()){
runApplication_IOS();
}
else {
runApplication_ANDROID();
}
logToServer('App launched');
}
function runApplication_ANDROID(){
OF.mobile.UI.Spinner.show();
OF.mobile.UI.TabGroup.closeAllWindows();
if(OF.mobile.UI.TabGroup.getComponent().getMainWindow()){
OF.mobile.UI.TabGroup.resetTabGroup();
OF.mobile.UI.create_dashboard(OF.mobile.UI.TabGroup.getComponent().getMainWindow());
}
else {
OF.mobile.UI.createWindow({
_url : 'dashboard.js',
navBarHidden : true,
exitOnClose: true,
addToControl : false,
ofRootWindow : true
});
}
//OF.mobile.UI.TabGroup.setActiveTab(OF.mobile.UI.TabGroup['new']);
OF.mobile.UI.TabGroup.open();
}
/**
* Show listview if accesskey is valid. Should only be called from app.js or from login callback function.
* @returns
*/
function runApplication_IOS()
{
var accesskey = get_string_from_cache(DEVICE_ID);
if (accesskey === null)
{
showLoginWindow();
return;
}
OF.mobile.UI.TabGroup.closeAllWindows();
if(Ti.App.tabgroup)
{
Ti.App.tv_today_obj.setNullState();
Ti.App.tv_new_obj.setNullState();
OF.mobile.UI.TabGroup.getComponent().tabs[OF.mobile.UI.TabGroup['new']].badge = null;
return OF.mobile.UI.TabGroup.setActiveTab(OF.mobile.UI.TabGroup['new']);
}
Titanium.UI.setBackgroundColor('#FFFF');
// tab group singleton
Ti.App.tabgroup = OF.mobile.UI.TabGroup;
var tvA = OF.mobile.UI.createWindow({
_url : 'tv_today.js',
title : "Today's Schedule",
fullscreen : false,
navBarHidden : true,
addToControl : false,
selectedView : VIEW_TODAY
});
var tvN = OF.mobile.UI.createWindow({
_url : 'tv_new.js',
title : "New Work Orders",
fullscreen : false,
navBarHidden : true,
addToControl : false
});
var mW = OF.mobile.UI.createWindow({
_url : 'more_window.js',
title : "More",
fullscreen : false,
navBarHidden : true,
addToControl : false
});
OF.mobile.UI.TabGroup.addTab(Titanium.UI.createTab({
icon : '/images/active_tabBarIcon.png',
title : 'Work Orders',
window : tvA,
ofWindow : tvA
}));
OF.mobile.UI.TabGroup.addTab(Titanium.UI.createTab({
icon : '/images/new_tabBarIcon.png',
title : 'New',
window : tvN,
ofWindow : tvN
}));
OF.mobile.UI.TabGroup.addTab(Titanium.UI.createTab({
icon : '/images/tabBar_more.png',
title : 'More',
window : mW,
ofWindow : mW
}));
OF.mobile.UI.TabGroup.setActiveTab(OF.mobile.UI.TabGroup['new']);
OF.mobile.UI.TabGroup.open({animated: true});
Ti.App.addEventListener('resume', function(){
if (Ti.App.countdown > 0)
{
clearInterval(Ti.App.timerButton.countdownSeconds);
OF.mobile.api.Workorder.getWaitingPeriod({
cb : function(t){
var response = t.jsonobj;
Ti.App.countdown = ((response.data.timer_periodleft.minutes * 60) + (response.data.timer_periodleft.seconds)) * 1000;
Ti.App.timerButton.title = parseInt(Ti.App.countdown/1000) + 's';
Ti.App.timerButton.addCountDownInterval();
},
params : {
workorderid : Ti.App.timerButton.workorderid
}
});
}
/**
* Wait a second for the device to establish context...
*/
var waitFor = setInterval(function() {
OF.mobile.UI.TabGroup.getComponent().fireEvent('blur');
clearInterval(waitFor);
},1000);
});
}
/**
* OF mobile-js base file
*
* @class OF core utilities and functions
* @singleton
*/
var OF = OF || {};
/**
* Copies all properties of sendingObj to receivingObj
*
* @param {Object} the receiver of properties
* @param {Object} the source of properties
* @param {Object} a different object that will be applied for defaults
* @return {Object}
*/
OF.apply = function(ro, so, defaults){
if(defaults){
OF.apply(ro, defaults);
}
if(ro && so && typeof so == 'object'){
for(var i in so){
ro[i] = so[i];
}
}
return ro;
};
/**
* Method to combine two objects and return the newly created combined object
*
* @param {Object} obj
* @param {Object} props
* @return {Object}
*/
OF.combine = function(obj, props){
var newObj = {};
OF.apply(newObj, obj);
return OF.apply(newObj, props);
}
OF.apply(Array.prototype, {
indexOf : function(o) {
for ( var i = 0, len = this.length; i < len; i++) {
if (this[i] == o)
return i;
}
return -1;
},
/**
* Removes the specified object from the array. If the object is not found nothing happens.
* @param {Object} o The object to remove
* @return {Array} this array
*/
remove : function(o){
var index = this.indexOf(o);
if(index != -1){
this.splice(index, 1);
}
return this;
},
removeIndex : function(index){
this.splice(index, 1);
return this;
}
});
OF.apply(String.prototype,{
capitalize : function(){
return this.replace( /(^|\s)([a-z])/g , function(m,p1,p2){ return p1+p2.toUpperCase(); } );
},
ellipsize : function(toLength) {
var ellipsis = '\u2026';
if (this.length < toLength) return this.substr(0, this.length); // returning "this" should be sufficient - but Titanium screws this up :(
return this.substr(0, toLength-1) + ellipsis;
},
//pads left
lpad : function(padString, length) {
var str = this;
while (str.length < length)
str = padString + str;
return str;
},
//pads right
rpad : function(padString, length) {
var str = this;
while (str.length < length)
str = str + padString;
return str;
},
/**
* Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens. Each
* token must be unique, and must increment in the format {0}, {1}, etc. Example usage:
* <pre><code>
var cls = 'my-class', text = 'Some text';
var s = String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
// s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
* </code></pre>
* @param {String} string The tokenized string to be formatted
* @param {String} value1 The value to replace token {0}
* @param {String} value2 Etc...
* @return {String} The formatted string
* @static
*/
format : function(){
var format = this;
var args = OF.toArray(arguments, 0);
return format.replace(/\{(\d+)\}/g, function(m, i){
return args[i];
});
}
});
(function(){
var stripTagsRE = /<\/?[^>]+>/gi,
stripBR = /<br[^\s]*\/?[^>]+>/gi;
OF.apply(OF, {
isIOS : function(){
return (OSNAME == OSNAME_IPHONE)
},
isAndroid : function(){
return (OSNAME == OSNAME_ANDROID)
},
includes : [],
include : function(){
for(var i = 0; i < arguments.length; i++){
if(OF.includes.indexOf(arguments[i]) < 0){
//Titanium.API.info('{guto} adding file '+arguments[i]);
Titanium.include('/' + arguments[i]);//doesnt work on 1.75
OF.includes.push(arguments[i]);
}
}
},
/**
* Copies all the properties of config to obj if they don't already exist.
* @param {Object} obj The receiver of the properties
* @param {Object} config The source of the properties
* @return {Object} returns obj
*/
applyIf : function(o, c){
if(o){
for(var p in c){
if(OF.isEmpty(o[p])){
o[p] = c[p];
}
}
}
return o;
},
USE_NATIVE_JSON : false,
/**
* Extends one class with another class and optionally overrides members with the passed literal. This class
* also adds the function "override()" to the class that can be used to override
* members on an instance.
* * <p>
* This function also supports a 2-argument call in which the subclass's constructor is
* not passed as an argument. In this form, the parameters are as follows:</p><p>
* <div class="mdetail-params"><ul>
* <li><code>superclass</code>
* <div class="sub-desc">The class being extended</div></li>
* <li><code>overrides</code>
* <div class="sub-desc">A literal with members which are copied into the subclass's
* prototype, and are therefore shared among all instances of the new class.<p>
* This may contain a special member named <tt><b>constructor</b></tt>. This is used
* to define the constructor of the new class, and is returned. If this property is
* <i>not</i> specified, a constructor is generated and returned which just calls the
* superclass's constructor passing on its parameters.</p></div></li>
* </ul></div></p><p>
* For example, to create a subclass of the OF GridPanel:
* <pre><code>
MyGridPanel = OF.extend(OF.grid.GridPanel, {
constructor: function(config) {
// Your preprocessing here
MyGridPanel.superclass.constructor.apply(this, arguments);
// Your postprocessing here
},
yourMethod: function() {
// etc.
}
});
</code></pre>
* </p>
* @param {Function} subclass The class inheriting the functionality
* @param {Function} superclass The class being extended
* @param {Object} overrides (optional) A literal with members which are copied into the subclass's
* prototype, and are therefore shared between all instances of the new class.
* @return {Function} The subclass constructor.
* @method extend
*/
extend : function(){
// inline overrides
var io = function(o){
for(var m in o){
this[m] = o[m];
}
};
var oc = Object.prototype.constructor;
return function(sb, sp, overrides){
if(OF.isObject(sp)){
overrides = sp;
sp = sb;
sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};
}
var F = function(){},
sbp,
spp = sp.prototype;
F.prototype = spp;
sbp = sb.prototype = new F();
sbp.constructor=sb;
sb.superclass=spp;
if(spp.constructor == oc){
spp.constructor=sp;
}
sb.override = function(o){
OF.override(sb, o);
};
sbp.superclass = sbp.supr = (function(){
return spp;
});
sbp.override = io;
OF.override(sb, overrides);
sb.extend = function(o){OF.extend(sb, o);};
return sb;
};
}(),
/**
* Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
* Usage:<pre><code>
OF.override(MyClass, {
newMethod1: function(){
// etc.
},
newMethod2: function(foo){
// etc.
}
});
</code></pre>
* @param {Object} origclass The class to override
* @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
* containing one or more methods.
* @method override
*/
override : function(origclass, overrides){
if(overrides){
var p = origclass.prototype;
OF.apply(p, overrides);
}
},
/**
* Takes an object and converts it to an encoded URL. e.g. OF.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2". Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
* @param {Object} o
* @param {String} pre (optional) A prefix to add to the url encoded string
* @return {String}
*/
urlEncode : function(o, pre){
var empty,
buf = [],
e = encodeURIComponent;
OF.iterate(o, function(key, item){
empty = OF.isEmpty(item);
OF.each(empty ? key : item, function(val){
buf.push('&', e(key), '=', (!OF.isEmpty(val) && (val != key || !empty)) ? (OF.isDate(val) ? OF.encode(val).replace(/"/g, '') : e(val)) : '');
});
});
if(!pre){
buf.shift();
pre = '';
}
return pre + buf.join('');
},
/**
* Taken from Ext Core
*
* Iterates either the elements in an array, or each of the properties in an object.
* <b>Note</b>: If you are only iterating arrays, it is better to call {@link #each}.
* @param {Object/Array} object The object or array to be iterated
* @param {Function} fn The function to be called for each iteration.
* The iteration will stop if the supplied function returns false, or
* all array elements / object properties have been covered. The signature
* varies depending on the type of object being interated:
* <div class="mdetail-params"><ul>
* <li>Arrays : <tt>(Object item, Number index, Array allItems)</tt>
* <div class="sub-desc">
* When iterating an array, the supplied function is called with each item.</div></li>
* <li>Objects : <tt>(String key, Object value, Object)</tt>
* <div class="sub-desc">
* When iterating an object, the supplied function is called with each key-value pair in
* the object, and the iterated object</div></li>
* </ul></div>
* @param {Object} scope The scope (<code>this</code> reference) in which the specified function is executed. Defaults to
* the <code>object</code> being iterated.
*/
iterate : function(obj, fn, scope){
if(OF.isEmpty(obj)){
return;
}
if(OF.isIterable(obj)){
OF.each(obj, fn, scope);
return;
}else if(OF.isObject(obj)){
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
if(fn.call(scope || obj, prop, obj[prop], obj) === false){
return;
};
}
}
}
},
isIterable : function(v){
//check for array or arguments
if(OF.isArray(v) || v.callee){
return true;
}
//check for node list type
if(/NodeList|HTMLCollection/.test(toString.call(v))){
return true;
}
//NodeList has an item and length property
//IXMLDOMNodeList has nextNode method, needs to be checked first.
return ((typeof v.nextNode != 'undefined' || v.item) && OF.isNumber(v.length));
},
/**
* Takes an encoded URL and and converts it to an object. Example: <pre><code>
OF.urlDecode("foo=1&bar=2"); // returns {foo: "1", bar: "2"}
OF.urlDecode("foo=1&bar=2&bar=3&bar=4", false); // returns {foo: "1", bar: ["2", "3", "4"]}
</code></pre>
* @param {String} string
* @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
* @return {Object} A literal with members
*/
urlDecode : function(string, overwrite){
var obj = {},
pairs = string.split('&'),
d = decodeURIComponent,
name,
value;
for(var i = 0; i < pairs.length; i++){
var pair = pairs[i].split('=');
name = d(pair[0]);
value = d(pair[1]);
obj[name] = overwrite || !obj[name] ? value :
[].concat(obj[name]).concat(value);
}
return obj;
},
/**
* Appends content to the query string of a URL, which handles logic for whether to place
* a question mark or ampersand.
* @param {String} url The url to append to.
* @@param {String} s The content to append to the url.
* @return (String) The appended string
*/
urlAppend : function(url, s){
if(!OF.isEmpty(s)){
return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
}
return url;
},
/**
* Converts any iterable (numeric indices and a length property) into a true array
* Don't use this on strings. IE doesn't support "abc"[0] which this implementation depends on.
* For strings, use this instead: "abc".match(/./g) => [a,b,c];
* @param {Iterable} the iterable object to be turned into a true Array.
* @return (Array) array
*/
toArray : function(){
return function(a, i, j){
return Array.prototype.slice.call(a, i || 0, j || a.length);
}
}(),
/**
* Iterates an array calling the passed function with each item, stopping if your function returns false. If the
* passed array is not really an array, your function is called once with it.
* The supplied function is called with (Object item, Number index, Array allItems).
* @param {Array/NodeList/Mixed} array
* @param {Function} fn
* @param {Object} scope
*/
each: function(array, fn, scope){
if(OF.isEmpty(array, true)){
return;
}
if(!OF.isIterable(array) || OF.isPrimitive(array)){
array = [array];
}
for(var i = 0, len = array.length; i < len; i++){
if(fn.call(scope || array[i], array[i], i, array) === false){
return i;
};
}
},
/**
* <p>Returns true if the passed value is empty.</p>
* <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
* <li>null</li>
* <li>undefined</li>
* <li>an empty array</li>
* <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
* </ul></div>
* @param {Mixed} value The value to test
* @param {Boolean} allowBlank (optional) true to allow empty strings (defaults to false)
* @return {Boolean}
*/
isEmpty : function(v, allowBlank){
return v === null || v === undefined || ((OF.isArray(v) && !v.length)) || (!allowBlank ? v === '' : false);
},
/**
* Returns true if the passed object is a JavaScript array, otherwise false.
* @param {Object} object The object to test
* @return {Boolean}
*/
isArray : function(v){
return toString.apply(v) === '[object Array]';
},
/**
* Returns true if the passed object is a JavaScript Object, otherwise false.
* @param {Object} object The object to test
* @return {Boolean}
*/
isObject : function(v){
return v && typeof v == "object";
},
/**
* Returns true if the passed object is a JavaScript 'primitive', a string, number or boolean.
* @param {Mixed} value The value to test
* @return {Boolean}
*/
isPrimitive : function(v){
return OF.isString(v) || OF.isNumber(v) || OF.isBoolean(v);
},
/**
* Returns true if the passed object is a JavaScript Function, otherwise false.
* @param {Object} object The object to test
* @return {Boolean}
*/
isFunction : function(v){
return toString.apply(v) === '[object Function]';
},
/**
* Returns true if the passed object is a number. Returns false for non-finite numbers.
* @param {Object} v The object to test
* @return {Boolean}
*/
isNumber: function(v){
return typeof v === 'number' && isFinite(v);
},
/**
* Returns true if the passed object is a string.
* @param {Object} v The object to test
* @return {Boolean}
*/
isString: function(v){
return typeof v === 'string';
},
/**
* Returns true if the passed object is a boolean.
* @param {Object} v The object to test
* @return {Boolean}
*/
isBoolean: function(v){
return typeof v === 'boolean';
},
/**
* Returns true if the passed object is not undefined.
* @param {Object} v The object to test
* @return {Boolean}
*/
isDefined: function(v){
return typeof v !== 'undefined';
},
/**
* Returns true if the passed object is not undefined.
* @param {Object} v The object to test
* @return {Boolean}
*/
isDate : function(v)
{
var result = false;
if(v.getTime && this.isNumber(v.getTime())){
result = true;
}
return result;
},
/**
* Rounds the passed number to the required decimal precision.
* @param {Number/String} value The numeric value to round.
* @param {Number} precision The number of decimal places to which to round the first parameter's value.
* @return {Number} The rounded value.
*/
round : function(value, precision) {
var result = Number(value);
if (!isNaN(result) && typeof precision == 'number') {
var decimals = precision;
precision = Math.pow(10, precision);
result = Math.round(value * precision) / precision;
result = result.toFixed(decimals);
}
return result;
},
/**
* Strips all HTML tags
* @param {Object} value The text from which to strip tags
* @return {String} The stripped text
*/
stripTags : function(v) {
return !v ? v : String(v).replace(stripTagsRE, "");
},
/**
* Strips all br tags and replace with \n
* @param {Object} value The text from which to strip tags
* @return {String} The stripped text
*/
stripBRTags : function(v) {
return !v ? v : String(v).replace(stripTagsRE, "\n");
}
});
/**
* @class OF.mobile
* @singleton
*/
OF.mobile = {
/**
* The version of the mobile lib
* @type String
*/
version : '1.0',
/**
* Creates namespaces to be used for scoping variables and classes so that they are not global.
* Specifying the last node of a namespace implicitly creates all other nodes. Usage:
* <pre><code>
OF.mobile.namespace('Company', 'Company.data');
OF.mobile.namespace('Company.data'); // equivalent and preferable to above syntax
Company.Widget = function() { ... }
Company.data.CustomStore = function(config) { ... }
</code></pre>
* @param {String} namespace1
* @param {String} namespace2
* @param {String} etc
* @method namespace
*/
namespace : function(){
var o, d;
OF.each(arguments, function(v) {
d = v.split(".");
o = d[0] = d[0] || {};
OF.each(d.slice(1), function(v2){
o = o[v2] = o[v2] || {};
});
});
return o;
}
};
/**
* Alias for namespaces to be used for scoping variables and classes so that they are not global.
* Specifying the last node of a namespace implicitly creates all other nodes. Usage:
* <pre><code>
OF.namespace('Company', 'Company.data');
OF.namespace('Company.data'); // equivalent and preferable to above syntax
Company.Widget = function() { ... }
Company.data.CustomStore = function(config) { ... }
</code></pre>
* @param {String} namespace1
* @param {String} namespace2
* @param {String} etc
* @method namespace
*/
OF.ns = OF.mobile.namespace;
})();
/**
* @class OF.util.JSON
* Modified version of Douglas Crockford"s json.js that doesn"t
* mess with the Object prototype
* http://www.json.org/js.html
* @singleton
*/
OF.util = OF.util || {};
OF.util.JSON = new (function(){
var useHasOwn = !!{}.hasOwnProperty,
isNative = function() {
var useNative = null;
return function() {
if (useNative === null) {
useNative = OF.USE_NATIVE_JSON && JSON && JSON.toString() == '[object JSON]';
}
return useNative;
};
}(),
pad = function(n) {
return n < 10 ? "0" + n : n;
},
doDecode = function(json){
return eval("(" + json + ')');
},
doEncode = function(o){
if(!OF.isDefined(o) || o === null){
return "null";
}else if(OF.isArray(o)){
return encodeArray(o);
}else if(OF.isDate(o)){
return OF.util.JSON.encodeDate(o);
}else if(OF.isString(o)){
return encodeString(o);
}else if(typeof o == "number"){
//don't use isNumber here, since finite checks happen inside isNumber
return isFinite(o) ? String(o) : "null";
}else if(OF.isBoolean(o)){
return String(o);
}else {
var a = ["{"], b, i, v;
for (i in o) {
// don't encode DOM objects
if(!o.getElementsByTagName){
if(!useHasOwn || o.hasOwnProperty(i)) {
v = o[i];
switch (typeof v) {
case "undefined":
case "function":
case "unknown":
break;
default:
if(b){
a.push(',');
}
a.push(doEncode(i), ":",
v === null ? "null" : doEncode(v));
b = true;
}
}
}
}
a.push("}");
return a.join("");
}
},
m = {
"\b": '\\b',
"\t": '\\t',
"\n": '\\n',
"\f": '\\f',
"\r": '\\r',
'"' : '\\"',
"\\": '\\\\'
},
encodeString = function(s){
if (/["\\\x00-\x1f]/.test(s)) {
return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
var c = m[b];
if(c){
return c;
}
c = b.charCodeAt();
return "\\u00" +
Math.floor(c / 16).toString(16) +
(c % 16).toString(16);
}) + '"';
}
return '"' + s + '"';
},
encodeArray = function(o){
var a = ["["], b, i, l = o.length, v;
for (i = 0; i < l; i += 1) {
v = o[i];
switch (typeof v) {
case "undefined":
case "function":
case "unknown":
break;
default:
if (b) {
a.push(',');
}
a.push(v === null ? "null" : OF.util.JSON.encode(v));
b = true;
}
}
a.push("]");
return a.join("");
};
/**
* <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
* <b>The returned value includes enclosing double quotation marks.</b></p>
* <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
* <p>To override this:</p><pre><code>
OF.util.JSON.encodeDate = function(d) {
return d.format('"Y-m-d"');
};
</code></pre>
* @param {Date} d The Date to encode
* @return {String} The string literal to use in a JSON string.
*/
this.encodeDate = function(o){
return '"' + o.getFullYear() + "-" +
pad(o.getMonth() + 1) + "-" +
pad(o.getDate()) + "T" +
pad(o.getHours()) + ":" +
pad(o.getMinutes()) + ":" +
pad(o.getSeconds()) + '"';
};
/**
* Encodes an Object, Array or other value
* @param {Mixed} o The variable to encode
* @return {String} The JSON string
*/
this.encode = function() {
var ec;
return function(o) {
if (!ec) {
// setup encoding function on first access
ec = isNative() ? JSON.stringify : doEncode;
}
return ec(o);
};
}();
/**
* Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
* @param {String} json The JSON string
* @return {Object} The resulting object
*/
this.decode = function() {
var dc;
return function(json) {
if (!dc) {
// setup decoding function on first access
dc = isNative() ? JSON.parse : doDecode;
}
return dc(json);
};
}();
})();
/**
* Shorthand for {@link OF.util.JSON#encode}
* @param {Mixed} o The variable to encode
* @return {String} The JSON string
* @member OF
* @method encode
*/
OF.encode = OF.util.JSON.encode;
/**
* Shorthand for {@link OF.util.JSON#decode}
* @param {String} json The JSON string
* @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
* @return {Object} The resulting object
* @member OF
* @method decode
*/
OF.decode = OF.util.JSON.decode;
OF.ns("OF.util", "OF.lib", "OF.mobile.UI");
/**
* @author Guto Dasilva
*/
/**
* This wrapper class will add some functionality to the Ti.UI.TabGroup
* and also manage the windows that are left open
*
* Upon a window creation through OF.mobile.UI.Window your instance
* will automatically be added to the tabgroup control that in turn will
* close all opened windows on logout or when a user press a single tab twice
*
* <pre>
* //close all open windows (beside root windows)
* OF.mobile.UI.TabGroup.closeAllWindows();
*
* //close windows within a tab
* OF.mobile.UI.TabGroup.closeWindows(tabIndex);
* </pre>
*
* @class OF.mobile.UI.TabGroup
* @singleton
*/
(function(){
/**
* Android tab object
*
*/
var androidTab = function(config){
OF.apply(this,{
active : false,
window : null,
badge : null,
_ui : null
});
config = config || {};
OF.apply(this, config);
};
androidTab.prototype = {
click : function(){
this.fireEvent('android-tab:click', this);
if(this.window)
this.window.open();
},
fireEvent : function(e, obj){
Ti.App.fireEvent(e, obj);
printLog('androidTab fake fireEvent {' + e + '}');
}
}
var createTab = function(config){
var tab = new androidTab(config);
tab._ui = new OF.mobile.UI.ActionListRow(OF.apply({
type : OF.mobile.UI.ActionListRow.typeDashboard},config));
return tab;
}
/**
* this is like a Ti.UI.TabGroup
*/
var androidTabgroup = function(){
this.tabs = [];
this.activeTab = null;
this._mainWindow = null;
this._ui = null;
}
var createTabGroup = function(){
return new androidTabgroup();
}
OF.apply(androidTabgroup.prototype, {
addMainWindow : function(window){
this._mainWindow = window;
},
addEventListener : function(event, handler){printLog('{adroidTabGroup} adding fake event ' + event);},
addTab : function(tab){
tab.ofWindow = this.getMainWindow();
var newIndex = this.tabs.length,
o = this;
var oldHandler = tab.handler || function(){};
tab.handler = function(e){
OF.mobile.UI.TabGroup.setActiveTab(e.index);
oldHandler.apply(this, Array.prototype.slice.call(arguments, 0));
OF.mobile.UI.TabGroup.getActiveTab().click();
};
var tab = createTab(tab);
this.tabs.push(tab);
return tab;
},
removeTab : function(tab){
this.tabs.remove(tab);
},
removeAllTabs : function(){
for(var i = 0; i < this.tabs.length;i++){
this.tabs.remove(tab);
}
},
setActiveTab : function(tabIndex){
for(var i = 0, l = this.tabs.length; i < l; i++){
if(this.tabs[i].active){
this.fireEvent('android-tabgroup:blur', {index : i, tab : this.tabs[i]});
}
this.tabs[i].active = false;
if(i == tabIndex){
this.fireEvent('android-tabgroup:focus', {index : i, tab : this.tabs[i]});
this.tabs[i].active = true;
}
}
},
getMainWindow : function(){
return this._mainWindow;
},
open : function() {
if(this._mainWindow) {
this._mainWindow.open();
//TODO split this later (add a setter)
this._ui = new OF.mobile.UI.ActionListDashboard({
window : this._mainWindow,
top : HEADER_HEIGHT,
addToView : OF.mobile.UI.createScrollView({
backgroundColor : '#4487AD',
top : HEADER_HEIGHT,
addToView : this._mainWindow
})
});
for(var i = 0; i < this.tabs.length; i++) {
this._ui.addRow(this.tabs[i]._ui);
}
Ti.App.fireEvent('android-tabgroup:open', {
event : 'open'
})
} else {
throw 'Main window of tabgroup needs to be set {androidTabgroup}';
}
},
fireEvent : function(e, obj){
Ti.App.fireEvent(e, obj);
printLog('androidTabgroup fake fireEvent {' + e + '}');
}
});
/**
* Wrapper to decide which creator to use
*
*/
OF.mobile.UI.Tab = function(config){
OF.mobile.UI.Tab.superclass.constructor.call(this, config);
this.creator = OF.isIOS() ? Titanium.UI.createTab : createTab;
this._init();
}
OF.extend(OF.mobile.UI.Tab, OF.mobile.UI.Component,{
createTab : function(config){
var tab = new OF.mobile.UI.Tab(config);
return tab._cp;
}
});
/**
* @private
*/
var tabGroup = function(){
/**
* Variable to hold the component creator
* @access protected
*/
this.creator = OF.isIOS() ? Titanium.UI.createTabGroup : createTabGroup;
/**
* @private
*/
this._windows = [];
this._init();
}
OF.extend(tabGroup, OF.mobile.UI.Component,{
/**
* constants for the tabs index
*/
accepted : 0,
'new' : 1,
more : 2,
today : 3,
acceptedColor : $$.tabColor.accepted,
newColor : $$.tabColor['new'],
moreColor : $$.tabColor.more,
/**
* @private Initializer
*
*/
_init : function(){
tabGroup.superclass._init.call(this);
/**
* this variable should hold date that the blur last ran, for some reason
* on startup every tab that is added it creates a blur event and we need to prevent multiple
* event firing
*/
var lastcalled = null;
var o = this;
function caseEvents(e)
{
if(!e.bypass && (lastcalled && lastcalled.clone().addMilliseconds(500).compareTo(new Date()) == 1)){
return;
}
if(!e.index && e.source.activeTab){
for(var i = 0; i < e.source.tabs.length; i++){
if(e.source.tabs[i].active){
e.index = i;
break;
}
}
}
switch (e.index)
{
case o['new']:
//printLog('firing new ' + o.canRefreshTab(o['new']));
if (o.canRefreshTab(o['new']))
Ti.App.fireEvent('refresh-new-list');
break;
case o.accepted:
//printLog('firing accept '+o.canRefreshTab(o.accepted));
if (o.canRefreshTab(o.accepted))
Ti.App.fireEvent('refresh-list');
break;
case o.more:
//printLog('firing More '+o.canRefreshTab(o.more));
if (o.canRefreshTab(o.more))
Ti.App.fireEvent('refresh-more');
break;
}
lastcalled = new Date();
}
this._cp.addEventListener('blur', function(e)
{
OF.isIOS() ? caseEvents(e) : '';
});
this._cp.addEventListener('focus', function(e){
OF.isAndroid() ? caseEvents(e) : '';
if(e.index == e.previousIndex){
OF.mobile.UI.TabGroup.closeWindows(e.index);
//focus comes after blur, so workaround to force refresh
OF.mobile.UI.TabGroup.getComponent().fireEvent('blur',{ bypass : true});
}
})
},
/**
* Method to see if a tab can be refreshed
*
* @public
* @return boolean
*/
canRefreshTab : function(index){
if(OF.isDefined(this._windows[index])){
return (this._windows[index].length > 0 ? false : true)
}
return null;
},
/**
* Method to set the active tab
*
* @public
* return void
*/
setActiveTab : function(tabIndex){
//Ti.API.info( JSON.stringify( this ) );
Ti.API.info('======');
//Ti.API.info( JSON.stringify(this._cp));
if(OF.isDefined(this._cp.tabs[tabIndex])){
return this._cp.setActiveTab(tabIndex);
}
throw '{tabgroup} No tabs found in index: ' + tabIndex;
},
/*
[INFO] OnForce/1.0 (2.1.1.GA.0fd84a2)
2012-08-01 22:27:40.126 OnForce[21067:1b503] +[TiWindowProxy boundBridge:withKrollObject:]: unrecognized selector sent to class 0x4d11d0
2012-08-01 22:28:11.592 OnForce[21067:1b503] +[TiWindowProxy unboundBridge:]: unrecognized selector sent to class 0x4d11d0
2012-08-01 22:28:11.595 OnForce[21067:1b503] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[TiWindowProxy unboundBridge:]: unrecognized selector sent to class 0x4d11d0'
*** First throw call stack:
(0x2f75022 0x2d95cd6 0x2f76aad 0x2edbed0 0x2edbcb2 0x4a6a1 0x57de2 0x26ca4c 0x2a4daa 0x2a5cd6 0x2a5c31 0x26b6b1 0x4faeb 0x50ae2 0x78c4d6 0x78c447 0x99a5ded9 0x99a616de)
terminate called throwing an exception
*/
/**
* Method to open the tabgroup
*
* @public
* @return void
*/
open : function(obj){
return this._cp.open(obj);
},
/**
* Method to add a tab to the tabgroup
*
* @public
* @return void
*/
addTab : function(tab){
//printLog('this._windows.length ' + this._windows.length);
this._windows.push([]);
//printLog('this._windows.length ' + this._windows.length);
this._cp.addTab(tab);
//printLog('added a tab: this._cp.tabs.length '+ this._cp.tabs.length);
},
/**
* Method to the current active tab
*
* @public
* @return Ti.UI.Tab
*/
getActiveTab : function(){
var index = this.getActiveTabIndex();
if(index !== null)
return this._cp.tabs[index];
return null;
},
getActiveTabColor : function(){
var index = this.getActiveTabIndex(),
color = OF.mobile.UI.TabGroup.moreColor;
switch (index) {
case OF.mobile.UI.TabGroup.today:
case OF.mobile.UI.TabGroup.accepted:
color = OF.mobile.UI.TabGroup.acceptedColor;
break;
case OF.mobile.UI.TabGroup['new'] :
color = OF.mobile.UI.TabGroup.newColor;
}
return color;
},
/**
* Method to get the active tab index
*
* @public
* @return int
*/
getActiveTabIndex : function(){
if(this._cp.tabs){
for(var i = 0; i < this._cp.tabs.length; i++){
if(this._cp.tabs[i].active){
return i;
}
}
}
return null;
},
/**
* Method to add the window to the control system
*
* @public
* @return void
*/
addWindow : function(window){
var i = this.getActiveTabIndex();
if(i === null){
i = OF.mobile.UI.TabGroup.accepted;
}
this._windows[i].push(window);
//printLog('{tabgroup.js} adding window ' + window._url + ' to index ' + i +' windows.length ' + this._windows[i].length);
},
/**
* Method to remove a window from the control system
*
* @public
* @return void
*/
removeWindow : function(){
var index = this.getActiveTabIndex();
if(index !== null){
//this._cp.tabs[index].ofWindow.remove(this._windows[index][this._windows[index].length -1]);//removing the rigth view
//printLog('{tabgroup.js} before pop ' + this._windows[index].length);
this._windows[index].pop();
//printLog('{tabgroup.js} after pop ' + this._windows[index].length);
}
},
getLastWindowInTab : function(){
var index = this.getActiveTabIndex();
printLog('{tabgroup getlastWindowInTab} ' + index);
if(index !== null){
return this._windows[index][this._windows[index].length -1];
}
throw '{tabgroup.js} no window to return';
},
/**
* Method to close windows in a tab
*
* @public
* @return void
*/
closeWindows : function(tabIndex){
if(this._windows[tabIndex]){
for(var i = 0; i < this._windows[tabIndex].length;i++){
this._windows[tabIndex][i].ofClose({bypassTabgroupRemove : true});
}
this._windows[tabIndex] = [];
}
},
/**
* Method to close all windows in a tabgroup
*
* @public
* @void
*/
closeAllWindows : function(){
for(var i = 0; i < this._windows.length; i++){
this.closeWindows(i);
}
}
});
/**
* Android specific methods
*/
if(OF.isAndroid()){
OF.apply(tabGroup.prototype, {
/**
* constants for the tabs index
*/
'new' : 0,
accepted : 1,
today : 2,
more : 3,
/**
* Method to set a bab
*
* @public
*/
setTab : function(tab, index){
this.resetTab(tab);
this.addTab(tab);
},
/**
* Method to set the window for this tab
*
* @public
*/
setTabWindow : function(window, index){
this._cp.tabs[index].window = window;
},
/**
* Method to reset a specific tab
*
* @public
*/
resetTab : function(tab){
var index = this._cp.tabs.indexOf(tab);
this.closeWindows(index);
this._cp.removeTab(tab);
this._windows.removeIndex(index);
},
/**
* Method to reset the entire tabgroup
*
* @public
*/
resetTabGroup : function(){
this.closeAllWindows();
this._windows = [];
this._cp.tabs = []
}
});
}
OF.mobile.UI.TabGroup = new tabGroup();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment