module.exports = $.View('AppDev.UI.AppTabGroup', {}, {
    init: function(options) {
        // Create the tab group
        var tabGroup = Ti.UI.createTabGroup();
        this._super({view: tabGroup});
    },
    
    // Create the tab group's tabs
    create: function() {
        this.options.windows.forEach(function(windowModule) {
            // Create the tab that will be associated with the window
            var tab = Ti.UI.createTab();
            
            // Load the window's module, and create a new window instance
            var Window = require('ui/'+windowModule);
            var $window = new Window({tab: tab});
            var window = $window.getWindow();
            
            // Assumes that window class names are in the format App???Window
            var matches = /^App(\w+)Window$/.exec(windowModule);
            var icon = matches ? '/images/'+matches[1].toLowerCase()+'.png' : null;
            
            // Add the window to the tab and add the tab to the tab group
            tab.window = window;
            tab.title = window.title;
            tab.icon = icon;
            this.view.addTab(this.record(windowModule, tab));
        }, this);
    },
    
    // Initialize the tab group
    initialize: function() {
        if (AD.Platform.isAndroid) {
            this.addEventListener('focus', function() {
                var actionBar = this.view.activity.actionBar;
                if (actionBar) {
                    actionBar.title = Ti.App.name;
                }
            });
        }
    },
    
    // Open the tab group
    open: function() {
        this.view.open();
    },
    
    // Close the tab group
    close: function() {
        this.view.close();
    },
    
    // Return the currently focused tab
    getActiveTab: function() {
        return this.view.activeTab;
    }
});
Example #2
0
$.View('jQuery.Window', {
    defaults: {
        createParams: {
            backgroundColor: 'white',
            exitOnClose: false
        }
    },
    actionShortcuts: {
        cancel: function() {
            // Cancel the task and close the window by rejecting the window's deferred
            this.dfd.reject();
        },
        preferences: function() {
            // Open the Android preferences window
            Ti.UI.Android.openPreferences();
        }
    },
    systemButtons: {
        add: Ti.UI.iPhone.SystemButton.ADD,
        edit: Ti.UI.iPhone.SystemButton.EDIT,
        cancel: Ti.UI.iPhone.SystemButton.CANCEL,
        done: Ti.UI.iPhone.SystemButton.DONE,
        save: Ti.UI.iPhone.SystemButton.SAVE
    }
}, {
    init: function(options) {
        var _this = this;
        
        // If 'init' is called via this._super(...) in a derived class, make sure that the new options are added to this.options
        $.extend(true, this.options, options);
        this.options.createParams.title = AD.localize(this.options.title); // can be either a string or a key in the locale file
        
        // This deferred object represents a possible pending task for the window and
        // will be resolved when the task completes, or rejected if the task is canceled
        this.dfd = $.Deferred();
        
        this.tab = this.tab || this.options.tab || (AD.UI.$appTabGroup && AD.UI.$appTabGroup.getActiveTab());
        
        // Create the window
        var window = this.window = Ti.UI.createWindow(this.options.createParams);
        
        // The window is the view's view
        this._super({ view: this.window });
        
        var leftNavButtons = [];
        var rightNavButtons = [];
        
        // Build a menu on Android and create nav buttons on iOS that correspond to each action
        var closeHandlers = [];
        var backButtonHandlers = [];
        var actions = $.mergeArrays(this.constructor.actions, this.actions, this.options.actions).filter(function(action) {
            var validPlatform = true;
            if (action.platform) {
                // Determine whether this is one of the platforms supported by the action
                validPlatform = action.platform.split('/').reduce(function(validPlatform, platform) {
                    return validPlatform || AD.Platform.is(platform);
                }, false);
            }
            var enabled = true;
            if (typeof action.enabled !== 'undefined') {
                // The enabled property can be a function or a boolean value
                enabled = $.isFunction(action.enabled) ? action.enabled.call(this) : action.enabled;
            }
            return validPlatform && enabled;
        }, this);
        actions.forEach(function(action) {
            // Expand the callback string to a shortcut function, if possible
            var shortcutAction = this.constructor.actionShortcuts[action.callback];
            if (shortcutAction && !this[action.callback]) {
                action.callback = shortcutAction;
            }
            
            // Ensure that inside the callback, 'this' will still refer to the window instance
            var callback = this.proxy(action.callback);
            
            if (AD.Platform.isiOS) {
                var button = {
                    title: action.title,
                    systemButton: action.systemButton,
                    callback: callback
                };
                if (action.leftNavButton === true || ($.isFunction(action.leftNavButton) && action.leftNavButton.call(this))) {
                    leftNavButtons.push(button);
                }
                if (action.rightNavButton === true || ($.isFunction(action.rightNavButton) && action.rightNavButton.call(this))) {
                    rightNavButtons.push(button);
                }
            }
            else if (AD.Platform.isAndroid) {
                if (action.backButton) {
                    backButtonHandlers.push(callback);
                }
            }
            
            if (action.onClose) {
                closeHandlers.push(callback);
            }
        }, this);
        
        // Close the window by resolving the deferred, but give onClose actions
        // a chance to prevent the window from closing by returning false
        var closeWindow = function() {
            if (!runCloseHandlers()) {
                _this.dfd.resolve();
            }
        };
        
        // Call each of the window's close handlers from its actions
        var runCloseHandlers = function() {
            var dontClose = false;
            closeHandlers.forEach(function(callback) {
                // If the close handler returned false, then do not close the window
                dontClose = dontClose || (callback() === false);
            });
            return dontClose;
        };
        
        window.addEventListener('close', function() {
            if (closeHandlers.length > 0) {
                // Call each of the onClose action callbacks when the window is
                // closed BY THE USER. DO NOT run the close handlers if the deferred
                // is already resolved or rejected, signaling that the window was
                // closed programmatically rather than by the user.
                
                if (_this.dfd.state() === 'pending') {
                    // At this point it is too late the prevent the window
                    // from closing, even if runCloseHandlers returned true
                    runCloseHandlers();
                }
            }
            else {
                // The default close handler resolves the deferred
                _this.dfd.resolve();
            }
            // Notify the view that the window is being destroyed
            _this.destroy();
        });
        
        window.addEventListener('android:back', function() {
            if (backButtonHandlers.length > 0) {
                // Call each of the backButton action callbacks
                backButtonHandlers.forEach(function(callback) {
                    callback();
                });
            }
            else {
                // The default back button handler closes the window
                closeWindow();
            }
        });
        
        if (AD.Platform.isiOS) {
            this.createNavButtons(rightNavButtons, 'Right');
            this.createNavButtons(leftNavButtons, 'Left');
        }
        
        if (AD.Platform.isAndroid) {
            window.addEventListener('focus', function() {
                // As of Titanium SDK 3.0.0, each window in a tab group shares a common activity.
                // Thus, the Android menu will need to be recreated each time the window changes.
                // See http://developer.appcelerator.com/blog/2012/12/breaking-changes-in-titanium-sdk-3-0.html
                var activity = window.activity;
                if (!activity || !activity.invalidateOptionsMenu) {
                    // This window does not have a real activity associated with it (its "activity" property might
                    // simply be an empty Javascript object), so get the activity from the application tab group
                    var tabGroup = AD.UI.$appTabGroup ? AD.UI.$appTabGroup.getView() : null;
                    activity = tabGroup ? tabGroup.activity : null;
                    if (!activity || !activity.invalidateOptionsMenu) {
                        // If we still cannot find a valid activity, then abort
                        return;
                    }
                }
                activity.onCreateOptionsMenu = function(event) {
                    // When a menu needs to be created in response to a press of the 'menu' button, create a menu item for each action
                    actions.forEach(function(action) {
                        // The menuItem property, if not specified, defaults to true
                        if (action.menuItem !== false) {
                            var menuItem = event.menu.add({
                                title: AD.localize(action.title),
                                showAsAction: action.showAsAction ? Ti.Android.SHOW_AS_ACTION_ALWAYS : Ti.Android.SHOW_AS_ACTION_NEVER,
                                icon: action.icon
                            });
                            menuItem.addEventListener('click', _this.proxy(action.callback));
                        }
                    });
                };
                activity.invalidateOptionsMenu();
                
                var actionBar = activity.actionBar;
                if (actionBar && _this.options.parent) {
                    actionBar.displayHomeAsUp = true;
                    actionBar.onHomeIconItemSelected = closeWindow;
                }
            });
        }
        
        if (this.options.focusedChild) {
            window.addEventListener('open', this.proxy(function() {
                // Focus the child specified by the focusedChild option
                this.getChild(this.options.focusedChild).focus();
            }));
        }
        
        if (this.options.autoOpen) {
            this.open();
        }
        
        // Close the window after the task is completed or canceled
        this.dfd.always(this.proxy('close'));
    },
    
    // Add buttons to the nav bar on the specied side
    createNavButtons: function(navButtons, side) {
        var navBarView = null;
        if (navButtons.length === 0) {
            return;
        }
        else if (navButtons.length === 1) {
            // Create a button as the nav bar view
            
            var navButton = navButtons[0];
            // Guess what system button this button represents based on its title
            var systemButton = this.constructor.systemButtons[navButton.title];
            var button = Ti.UI.createButton({
                title: AD.localize(navButton.title),
                systemButton: navButton.systemButton || systemButton // will be undefined in some cases
            });
            button.addEventListener('click', navButton.callback);
            navBarView = button;
        }
        else {
            // Create multiple buttons and add them to a button bar as the nav bar view
            var labels = navButtons.map(function(button) { return AD.localize(button.title); });
            var buttonBar = Ti.UI.createButtonBar({
                labels: labels,
                style: Ti.UI.iPhone.SystemButtonStyle.BAR
            });
            buttonBar.addEventListener('click', function(event) {
                var button = navButtons[event.index];
                if (button) {
                    // If the user clicked the button bar, but not a button,
                    // then event.index is null and button will be undefined
                    button.callback();
                }
            });
            navBarView = buttonBar;
        }
        
        this.window['set'+side+'NavButton'](navBarView);
    },
    
    isOpen: false,
    
    // Open the window
    open: function() {
        this.isOpen = true;
        if (this.options.modal) {
            if (AD.Platform.isiOS) {
                // On iOS, open modal windows within a navigation window
                this.navWindow = Ti.UI.iOS.createNavigationWindow({
                    window: this.window
                });
                this.navWindow.open();
            }
            else {
                this.window.open({
                    fullscreen: true
                });
            }
        }
        else if (this.tab) {
            this.tab.open(this.window);
        }
        else {
            this.window.open();
        }
    },
    close: function() {
        this.isOpen = false;
        if (this.navWindow) {
            this.navWindow.close();
        }
        else if (this.tab && !AD.Platform.isAndroid) {
            this.tab.close(this.window, { animated: true });
        }
        else {
            this.window.close();
        }
    },
    
    // Shortcut for creating a window of the specified type
    // The primary advantage is that it automatically specifies the parent of the new window
    // This:
    //     var $window = this.createWindow('WindowName', { param: value });
    // Is equivalent to this:
    //     var $window = new AD.UI.WindowName({ parent: this, param: value });
    createWindow: function(type, params) {
        var Constructor = $.isFunction(type) ? type : $.String.getObject(type, AD.UI);
        var $window = new Constructor($.extend({
            parent: this
        }, params));
        return $window;
    },
    
    isWindow: true, // is a member of the $.Window class
    
    // Enable/disable the view
    // Override the default $.View functionality which sets the "opacity"
    // property. This would create a window that uses a translucent
    // theme. A disabled window does not really make sense anyway.
    setEnabled: function(enabled) {
        this.enabled = enabled;
    },
    
    // Return the deferred object representing the window's operation
    getDeferred: function() {
        return this.dfd;
    },
    
    // Return the window view
    getWindow: function() {
        return this.window;
    }
});
Example #3
0
$.View('jQuery.Window', {
    defaults: {
        createParams: {
            backgroundColor: 'white'
        },
        activity: {} // for Android
    },
    systemButtons: {
        edit: Ti.UI.iPhone.SystemButton.EDIT,
        cancel: Ti.UI.iPhone.SystemButton.CANCEL,
        done: Ti.UI.iPhone.SystemButton.DONE,
        save: Ti.UI.iPhone.SystemButton.SAVE
    }
}, {
    init: function(options) {
        var _this = this;
        
        // If 'init' is called via this._super(...) in a derived class, make sure that the new options are added to this.options
        $.extend(true, this.options, options);
        this.options.createParams.title = AD.Localize(this.options.title); // can be either a string or a key in the locale file
        
        this.tab = this.tab || this.options.tab;
        
        // Create the window
        var window = this.window = Ti.UI.createWindow(this.options.createParams);
        
        // The window is the view's view
        this._super({ view: this.window });
        
        var leftNavButtons = [];
        var rightNavButtons = [];
        
        // Build a menu on Android and create nav buttons on iOS that correspond to each action
        var hasCloseHandler = false;
        var actions = $.mergeArrays(this.constructor.actions, this.actions, this.options.actions).filter(function(action) {
            var validPlatform = true;
            if (action.platform) {
                // Determine whether this is one of the platforms supported by the action
                validPlatform = action.platform.split('/').reduce(function(validPlatform, platform) {
                    return validPlatform || AD.Platform.is(platform);
                }, false);
            }
            var enabled = true;
            if (typeof action.enabled !== 'undefined') {
                // The enabled property can be a function or a boolean value
                enabled = $.isFunction(action.enabled) ? action.enabled.call(this) : action.enabled;
            }
            return validPlatform && enabled;
        }, this);
        actions.forEach(function(action) {
            if (action.callback === 'cancel') {
                // Special shortcut to cancel the task and close the window by rejecting the window's deferred
                action.callback = function() {
                    this.dfd.reject();
                };
            }
            
            // Ensure that inside the callback, 'this' will still refer to the window instance
            var callback = this.proxy(action.callback);
            
            if (AD.Platform.isiOS) {
                var button = {
                    title: action.title,
                    systemButton: action.systemButton,
                    callback: callback
                };
                if (action.leftNavButton === true || ($.isFunction(action.leftNavButton) && action.leftNavButton.call(this))) {
                    leftNavButtons.push(button);
                }
                if (action.rightNavButton === true || ($.isFunction(action.rightNavButton) && action.rightNavButton.call(this))) {
                    rightNavButtons.push(button);
                }
            }
            else if (AD.Platform.isAndroid) {
                if (action.backButton) {
                    window.addEventListener('android:back', callback);
                }
            }
            
            if (action.onClose) {
                if (hasCloseHandler) {
                    console.warn('Window already has close handler!');
                }
                else {
                    // Call the callback when the window is closed
                    hasCloseHandler = true;
                    window.addEventListener('close', callback);
                }
            }
        }, this);
        
        if (AD.Platform.isiOS) {
            this.createNavButtons(rightNavButtons, 'Right');
            this.createNavButtons(leftNavButtons, 'Left');
        }
        
        if (AD.Platform.isAndroid) {
            window.activity.onCreateOptionsMenu = function(event) {
                // When a menu needs to be created in response to a press of the 'menu' button, create a menu item for each action
                var menu = event.menu;
                actions.forEach(function(action) {
                    // The menuItem property, if not specified, defaults to true
                    if (action.menuItem !== false) {
                        var menuItem = menu.add({ title: AD.Localize(action.title) });
                        menuItem.addEventListener('click', _this.proxy(action.callback));
                    }
                });
            };
        }
        
        // This deferred object represents a possible pending task for the window and
        // will be resolved when the task completes, or rejected if the task is canceled
        this.dfd = $.Deferred();
        
        if (!hasCloseHandler) {
            // The default close handler rejects the deferred
            window.addEventListener('close', this.dfd.reject);
        }
        // Notify the view that the window is being destroyed
        window.addEventListener('close', this.proxy('destroy'));
        
        if (this.options.focusedChild) {
            window.addEventListener('open', this.proxy(function() {
                // Focus the child specified by the focusedChild option
                this.getChild(this.options.focusedChild).focus();
            }));
        }
        
        if (this.options.autoOpen) {
            this.open();
        }
        
        // Close the window after the task is completed or canceled
        this.dfd.always(this.proxy('close'));
    },
    
    // Add buttons to the nav bar on the specied side
    createNavButtons: function(navButtons, side) {
        var navBarView = null;
        if (navButtons.length === 0) {
            return;
        }
        else if (navButtons.length === 1) {
            // Create a button as the nav bar view
            
            var navButton = navButtons[0];
            // Guess what system button this button represents based on its title
            var systemButton = this.constructor.systemButtons[navButton.title];
            var button = navBarView = Ti.UI.createButton({
                title: AD.Localize(navButton.title),
                systemButton: navButton.systemButton || systemButton // will be undefined in some cases
            });
            button.addEventListener('click', navButton.callback);
        }
        else {
            // Create multiple buttons and add them to a button bar as the nav bar view
            var labels = navButtons.map(function(button) { return AD.Localize(button.title); });
            var buttonBar = navBarView = Ti.UI.createButtonBar({
                labels: labels,
                style: Ti.UI.iPhone.SystemButtonStyle.BAR
            });
            buttonBar.addEventListener('click', function(event) {
                var button = navButtons[event.index];
                if (button) {
                    // If the user clicked the button bar, but not a button,
                    // then event.index is null and button will be undefined
                    button.callback();
                }
            });
        }
        
        this.window['set'+side+'NavButton'](navBarView);
    },
    
    isOpen: false,
    
    // Open the window
    open: function() {
        this.isOpen = true;
        if (this.options.modal) {
            this.window.open({ modal: true });
        }
        else if (this.tab) {
            this.tab.open(this.window);
        }
        else {
            this.window.open();
        }
    },
    close: function() {
        this.isOpen = false;
        if (this.tab && !AD.Platform.isAndroid) {
            this.tab.close(this.window, { animated: true });
        }
        else {
            this.window.close();
        }
    },
    
    isWindow: true, // is a member of the $.Window class
    
    // Return the deferred object representing the window's operation
    getDeferred: function() {
        return this.dfd;
    },
    
    // Return the window view
    getWindow: function() {
        return this.window;
    }
});