API Docs for: HTML-14.04-dev~bzr202

pagestacks.js

/*
 * Copyright (C) 2013 Adnane Belmadiaf <daker@ubuntu.com>
 * License granted by Canonical Limited
 *
 * This file is part of ubuntu-html5-ui-toolkit.
 *
 * This package is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or
 * (at your option) any later version.

 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */

/**
 * The Pagestack manages all Pages in a stack data structure. Initially, the Pagestack contains no Pages. The <em>push()</em> method is normally executed on load to display the app starting page.

      UI.pagestack.push("pageID")

The topmost Page on the Pagestack is always displayed.

The Pagestack is declared as a direct child of the <em>content</em> div.

#####Default application wide footer
The Pagestack contains a default <em>footer</em> (represented in JavaScript as a Toolbar), even if you do not declare one in HTML. The <em>footer</em> has a single Back button.
#####Customized application wide footer
This application-wide <em>footer</em> can be customized (for example, you can add Buttons) by declaring a <em>footer</em> as a direct child of the <em>pagestack</em> div (see example).
######Page specific footers
A <em>page</em> may declare a page-specific <em>footer</em> as a child element.

 * @class Pagestack
 * @namespace UbuntuUI
 * @constructor
 * @example

     <div data-role="mainview">

        <header data-role="header">
        </header>

        <div data-role="content">

          <div data-role="pagestack">

            <div data-role="page" id="main" data-title="Page 1">
            </div>

            <div data-role="page" id="page2" data-title="Page 2">
              [...]
              <footer data-role="footer" class="revealed" id="footerPage2">
                [...]
              </footer>
            </div>

            <footer data-role="footer" class="revealed" id="footerAppWide">
              [...]
            </footer>

          </div>  <!-- end of Pagestack div -->

        </div>

      </div>

      JavaScript access:
      UI.pagestack.METHOD();

 */
var Pagestack = (function () {

    function __safeCall(f, args, errorfunc) {
        if (typeof (f) !== 'function')
            return;
        try {
            f.apply(null, args);
        } catch (e) {
            if (errorfunc && typeof (errorfunc) === 'function') errorfunc(e)
        }
    };

    function Pagestack(pageStack) {
        this._pages = [];
        this._pageStack = pageStack;
        this._backBtn = document.querySelector('[data-role="back-btn"]');
    };

    Pagestack.prototype = {
        /**
         * Push a page to the top of this pagestack
         * @method push
         * @param {String} id - The id attribute of the page element to be pushed
         * @param {Object} properties - A list of properties passed down to the page that is to be activated
         */
        push: function (id, properties) {
            try {
                __safeCall(this.__setAllPagesVisibility.bind(this), [false]);
                (new Page(id)).activate(properties);
                this._pages.push(id);

                this.__dispatchPageChanged(this.currentPage());
            } catch (e) {}
        },

        /**
         * Checks for zero pages in this pagestack
         * @method isEmpty
         * @return {Boolean} - True when this pagestack has no pages, else false
         */
        isEmpty: function () {
            return this._pages.length === 0;
        },

        /**
         * Gets the id attribute of the page element on top of this pagestack
         * @method currentPage
         * @return {PageID|Null} - The topmost page's id attribute, else null when there are no pages on this pagestack
         */
        currentPage: function () {
            return this.isEmpty() ? null : this._pages[this._pages.length - 1];
        },

        /**
         * Gets the number of pages in this pagestack
         * @method depth
         * @return {Number} - The number of pages in this pagestack
         */
        depth: function () {
            return this._pages.length;
        },

        /**
         * Clears the whole page stack
         * @method clear
         */
        clear: function () {
            if (this.isEmpty())
                return;
            __safeCall(Page.prototype.deactivate.bind(new Page(this.currentPage())), []);
            this._pages = [];
        },

        /**
         * Pops the current page off this pagestack, which causes the next page to become the top page and to display
         * @method pop
         */
        pop: function () {
            if (this.isEmpty())
                return;
            __safeCall(Page.prototype.deactivate.bind(new Page(this.currentPage())), []);
            this._pages.pop();
            __safeCall(Page.prototype.activate.bind(new Page(this.currentPage())), []);

            this.__dispatchPageChanged(this.currentPage());
        },

        onPageChanged : function(callback){
            this._pageStack.addEventListener("pagechanged", callback);
        },

        /**
         * @private
         */
        __setAllPagesVisibility: function (visible) {
            var visibility = visible ? "block" : "none";

            var children = [].slice.call(this._pageStack.children);
            children.forEach(function(element) {
                var pageHelper = new Page();
                if (pageHelper.isPage(element)) {
                    element.style.display = visibility;
                }
            });
        },

        /**
         * @private
         */
        __isPage: function (element) {
            return element.getAttribute('data-role') === 'page';
        },

        /**
         * @private
         */
        __dispatchPageChanged: function (page) {
            this._backBtn.disabled = (this.depth() ==  1);

            [].forEach.call( document.querySelectorAll('[data-role="actions-wrapper"]'), function(el) {
                el.style.display = 'none';
            });

            var pageActions = document.getElementById("actions_" + page);
            if (pageActions !== null) {
                pageActions.style.display = 'block';
            }

            var event = document.createEvent('Event');
            event.initEvent('pagechanged',true,true);
            event.page = page;
            this._pageStack.dispatchEvent(event);
        },
    };

    return Pagestack;
})();