API Docs for:
Show:

File: iframe-step.js

/*global YUI, alert, document*/
YUI.add("iframe-step", function (Y) {
    /**
     * A iframe step control provides a widget for browsing among frames horizontally in an overloaded page region.
     *
     * @module iframe-step
     * @requires widget
     */

    "use strict";

    var Lang = Y.Lang, // Y.Lang shortcut.
        //=================
        // Constants
        //=================
        MODULE_NAME = "iframestep", // For CSS namespace.
        MODULE_ID   = "iframe-step",
        CSS_CLASSES, // Used CSS class names.
        //=================
        // Private Methods
        //=================
        _getClassName,
        _log;


    _log = function (msg, type, module) {
        type = type || "info";
        module = module || MODULE_ID;
        Y.log(msg, type, module);
    };

    _getClassName = function (className) {
        return Y.ClassNameManager.getClassName(MODULE_NAME, className);
    };

    CSS_CLASSES = {
        "buttons"       : _getClassName("buttons"),
        "first"         : _getClassName("first"),
        "last"          : _getClassName("last"),
        "loading"       : _getClassName("loading"),
        "item"          : _getClassName("item"),
        "item-selected" : _getClassName("item-selected"),
        "next-button"   : _getClassName("next-button"),
        "prev-button"   : _getClassName("prev-button"),
        "end-button"    : _getClassName("end-button")
    };

    /**
     * The IframeStep provides a widget for browsing among iframes.
     *
     * @class IframeStep
     * @extends Widget
     * @param config {Object} Configuration options for the widget
     * @constructor
     */
    function IframeStep(config) {
        IframeStep.superclass.constructor.apply(this, arguments);
    }

    IframeStep.NAME = MODULE_NAME;
    IframeStep.ATTRS = {
        /**
         * Frames data collection.
         *
         * @attribute frames
         * @type {Object}
         */
        frames : {
            value: [],
            validator: Y.Lang.isObject
        },
        /**
         * Current offset.
         *
         * @attribute offset
         * @type {Number}
         * @default 0
         */
        offset : {
            value: 0,
            validator: function (value) {
                var that = this;
                return that._validateOffset(value);
            }
        },
        /**
         * The amount of iframes.
         *
         * @attribute offset
         * @type {Number}
         * @readOnly
         */
        total: {
            getter: function () {
                var that = this;
                return that.get("frames").length;
            },
            readOnly: true
        }
    };

    IframeStep.HTML_PARSER = {
        frames: function (srcNode) {
            var frames = [];
            srcNode.all("li").each(function (el) {
                frames.push({
                    title    : el.one("a").getHTML(),
                    url      : el.one("a").get("href"),
                    rendered : false
                });
            });
            return frames;
        }
    };

    Y.extend(IframeStep, Y.Widget, {
        //=================
        // Event Handlers
        //=================
        /**
         * Updates UI according to provided offset.
         *
         * @method _uiSetOffset
         * @private
         * @param offset {Number} The frame offset.
         */
        _uiSetOffset: function (offset) {
            _log("_uiSetOffset() is executed.");
            var that = this,
                node = that.get("boundingBox"),
                activeItem = node.one("." + CSS_CLASSES["item-selected"]),
                frames = that.get("frames"),
                frame = frames[offset],
                frameNode,
                itemNode = node.all("." + CSS_CLASSES.item).item(offset);

            // Deals with CSS classes of boundingBox.
            node.removeClass(CSS_CLASSES.first + "|" + CSS_CLASSES.last);
            if (offset === 0) {
                node.addClass(CSS_CLASSES.first);
            } else if (offset === frames.length - 1) {
                node.addClass(CSS_CLASSES.last);
            }

            // Renders a new iframe if it doesn't exist.
            if (!frame.rendered) {
                frameNode = Y.Node.create('<iframe src="' + frame.url + '"></iframe>');
                itemNode.append(frameNode);
                node.addClass(CSS_CLASSES.loading);
                frameNode.once("load", function (e) {
                    node.removeClass(CSS_CLASSES.loading);
                });
                frame.rendered = true;
            }

            // Provides appropriate class name to curren item.
            activeItem = node.one("." + CSS_CLASSES["item-selected"]);
            if (activeItem) {
                activeItem.removeClass(CSS_CLASSES["item-selected"]);
            }
            itemNode.addClass(CSS_CLASSES["item-selected"]);
        },
        /**
         * Handles offsetChange event.
         * It deals with CSS classes on boundingBox.
         *
         * @method _afterOffsetChange
         * @private
         * @param e {Y.Event} Event instance.
         */
        _afterOffsetChange : function (e) {
            _log("_afterOffsetChange() is executed.");
            var that = this;
            that._uiSetOffset(e.newVal);
        },
        //=================
        // Private Methods
        //=================
        /**
         * Validates if the provided offset is valid.
         *
         * @method _validateOffset
         * @private
         * @return {Boolean} false if it's illegal.
         */
        _validateOffset: function (offset) {
            _log("_validateOffset() is executed.");
            var that = this;
            if (!Lang.isNumber(offset)) {
                return false;
            }
            if (offset >= that.get("frames").length || offset < 0) {
                _log("The offset attribute value (" + offset + ") is not valid.", "warn");
                return false;
            }
            return true;
        },
        //===================
        // Protected Methods
        //===================
       /**
         * Creates the DOM structure for the IframeStep.
         *
         * @method renderUI
         * @protected
         */
        renderUI: function () {
            _log("renderUI() is executed.");
            var that = this,
                node;

            // Appends buttons.
            node = that.get("contentBox");
            node.all("li").addClass(CSS_CLASSES.item);
            node.append([
                '<div class="' + CSS_CLASSES.buttons + '">',
                '    <button class="yui3-button ' + CSS_CLASSES["prev-button"] + '">Prev</button>',
                '    <button class="yui3-button ' + CSS_CLASSES["next-button"] + '">Next</button>',
                '    <button class="yui3-button ' + CSS_CLASSES["end-button"] + '">End</button>',
                '<div>'
            ].join(""));
        },
        /**
         * Binds button interaction.
         *
         * @method bindUI
         * @protected
         */
        bindUI : function () {
            _log("bindUI() is executed.");
            var that = this,
                node;

            node = that.get("contentBox");
            node.one("." + CSS_CLASSES["prev-button"]).on("click", that.prev, that);
            node.one("." + CSS_CLASSES["next-button"]).on("click", that.next, that);
            node.one("." + CSS_CLASSES["end-button"]).on("click", function (e) {
                var that = this;
                that.fire("end");
            }, that);

            that.after("offsetChange", that._afterOffsetChange);
        },
        /**
         * Synchronizes the DOM state with the attribute settings.
         *
         * @method syncUI
         * @protected
         */
        syncUI: function () {
            var that = this,
                offset = that.get("offset");

            that._uiSetOffset(offset);
        },
        //=================
        // Public Methods
        //=================
        /**
         * Switches to specific item.
         *
         * @method move
         * @public
         * @param offset {Boolean|Number} true if it switches to next iframe.
         *     false if it switches to previous iframe.
         *     You can also provide a number of offset to show a specific frame.
         */
        move: function (offset) {
            _log("move(" + offset + ") is executed.");
            var that = this;

            // Converts and checks the offset argument.
            offset = (Lang.isString(offset)) ? parseInt(offset, 10) : offset;
            if (Lang.isBoolean(offset)) {
                offset = (offset) ? that.get("offset") + 1 : that.get("offset") - 1;
            }

             // This triggers the _afterOffsetChange event.
            that._set("offset", offset);
        },
        /**
         * Moves to next item.
         *
         * @method next
         * @public
         */
        next: function () {
            _log("next() is executed.");
            var that = this;
            that.move(true);
        },
        /**
         * Moves to previous item.
         *
         * @method prev
         * @public
         */
        prev: function () {
            _log("prev() is executed.");
            var that = this;
            that.move(false);
        }
    });

    Y.IframeStep = IframeStep;

}, "0.0.1", {requires: ["widget"]});