<commit_before>/*
 * Copyright (c) 2016. tom@axisj.com
 * - github.com/thomasjang
 * - www.axisj.com
 */

// ax5.ui.layout
(function (root, _SUPER_) {

    /**
     * @class ax5.ui.layout
     * @classdesc
     * @version 0.1.0
     * @author tom@axisj.com
     * @example
     * ```
     * var myLayout = new ax5.ui.layout();
     * ```
     */
    var U = ax5.util;

    //== UI Class
    var axClass = function () {
        var
            self = this,
            cfg,
            ENM = {
                "mousedown": (ax5.info.supportTouch) ? "touchstart" : "mousedown",
                "mousemove": (ax5.info.supportTouch) ? "touchmove" : "mousemove",
                "mouseup": (ax5.info.supportTouch) ? "touchend" : "mouseup"
            },
            getMousePosition = function (e) {
                var mouseObj = e;
                if ('changedTouches' in e) {
                    mouseObj = e.changedTouches[0];
                }
                return {
                    clientX: mouseObj.clientX,
                    clientY: mouseObj.clientY
                }
            };

        if (_SUPER_) _SUPER_.call(this); // 부모호출

        this.queue = [];
        this.config = {
            theme: 'default',
            animateTime: 250,
            splitter: {
                size: 5
            }
        };

        this.openTimer = null;
        this.closeTimer = null;
        this.resizer = null;

        cfg = this.config;

        var
            onStateChanged = function (opts, that) {
                if (opts && opts.onStateChanged) {
                    opts.onStateChanged.call(that, that);
                }
                else if (this.onStateChanged) {
                    this.onStateChanged.call(that, that);
                }
                return true;
            },
            alignLayoutAll = function () {
                var i = this.queue.length;
                while (i--) {
                    if (typeof this.queue[i].parentQueIdx === "undefined") alignLayout.call(this, i);
                }
            },
            alignLayout = (function () {

                var setCSS = {
                    'top': function (item, panel) {
                        panel.$target.css({height: panel.__height || 0});
                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({height: cfg.splitter.size});
                        }
                    },
                    'bottom': function (item, panel) {
                        panel.$target.css({height: panel.__height || 0});
                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({height: cfg.splitter.size});
                        }
                    },
                    'left': function (item, panel) {
                        var css = {
                            width: panel.__width,
                            height: item.targetDimension.height
                        };

                        if (item.dockPanel.top) {
                            css.height -= item.dockPanel.top.__height;
                            css.top = item.dockPanel.top.__height;
                        }
                        if (item.dockPanel.bottom) {
                            css.height -= item.dockPanel.bottom.__height;
                        }

                        panel.$target.css(css);

                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({width: cfg.splitter.size});
                        }
                    },
                    'right': function (item, panel) {
                        var css = {
                            width: panel.__width,
                            height: item.targetDimension.height
                        };

                        if (item.dockPanel.top) {
                            css.height -= item.dockPanel.top.__height;
                            css.top = item.dockPanel.top.__height;
                        }
                        if (item.dockPanel.bottom) {
                            css.height -= item.dockPanel.bottom.__height;
                        }

                        panel.$target.css(css);

                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({width: cfg.splitter.size});
                        }
                    },
                    'center': function (item, panel) {
                        var css = {
                            width: item.targetDimension.width,
                            height: item.targetDimension.height
                        };

                        if (item.dockPanel.top) {
                            css.height -= item.dockPanel.top.__height || 0;
                            css.top = item.dockPanel.top.__height || 0;
                        }
                        if (item.dockPanel.bottom) {
                            css.height -= item.dockPanel.bottom.__height || 0;
                        }
                        if (item.dockPanel.left) {
                            css.width -= item.dockPanel.left.__width || 0;
                            css.left = item.dockPanel.left.__width || 0;
                        }
                        if (item.dockPanel.right) {
                            css.width -= item.dockPanel.right.__width || 0;
                        }

                        panel.$target.css(css);
                    }
                };
                var childResize = function (item) {
                    var i = item.childQueIdxs.length;
                    while (i--) {
                        alignLayout.call(this, item.childQueIdxs[i]);
                    }
                };

                return function (queIdx, callBack) {

                    var item = this.queue[queIdx];

                    // 레이아웃 타겟의 CSS속성을 미리 저장해 둡니다. 왜? 패널별로 크기 계산 할 때 쓰려고
                    item.targetDimension = {
                        height: item.$target.innerHeight(),
                        width: item.$target.innerWidth()
                    };

                    // dockPanel
                    if (item.control == "dock-panel") {
                        for (var panel in item.dockPanel) {
                            if (item.dockPanel[panel].$target && item.dockPanel[panel].$target.get(0)) {
                                if (panel in setCSS) {
                                    setCSS[panel].call(this, item, item.dockPanel[panel]);
                                }
                            }
                        }
                    }

                    if (item.childQueIdxs) childResize.call(this, item);
                    if (item.onResize) {
                        setTimeout((function () {
                            this.onResize.call(this, this);
                        }).bind(item), 1)

                    }
                    if (callBack) {
                        callBack.call(item, item);
                    }
                }
            })(),
            bindLayoutTarget = (function () {

                var resizeSplitter = {
                    on: function (queIdx, panel) {

                        var splitterOffset = panel.$splitter.offset();
                        var splitterBox = {
                            width: panel.$splitter.width(), height: panel.$splitter.height()
                        };
                        var getResizerPosition = {
                            'left': function (e) {
                                return {left: panel.$splitter.offset().left + e.clientX - panel.mousePosition.clientX};
                            }
                        };

                        jQuery(document.body)
                            .bind(ENM["mousemove"] + ".ax5layout-" + this.instanceId, function (e) {
                                // console.log(e.clientX - panel.mousePosition.clientX);
                                if (!self.resizer) {
                                    self.resizer = jQuery('

');
                                    self.resizer.css({
                                        left: splitterOffset.left,
                                        top: splitterOffset.top,
                                        width: splitterBox.width,
                                        height: splitterBox.height
                                    });
                                    jQuery(document.body).append(self.resizer);
                                }
                                self.resizer.css(getResizerPosition[panel.dock](e));
                            })
                            .bind(ENM["mouseup"] + ".ax5layout-" + this.instanceId, function (e) {
                                resizeSplitter.off.call(self);
                            })
                            .bind("mouseleave.ax5layout-" + this.instanceId, function (e) {
                                resizeSplitter.off.call(self);
                            });
                    },
                    off: function () {
                        self.resizer.remove();
                        self.resizer = null;
                        jQuery(document.body)
                            .unbind(ENM["mousemove"] + ".ax5layout-" + this.instanceId)
                            .unbind(ENM["mouseup"] + ".ax5layout-" + this.instanceId)
                            .unbind("mouseleave.ax5layout-" + this.instanceId);
                    }
                };

                var applyLayout = {
                    'dock-panel': function (queIdx) {
                        var item = this.queue[queIdx], outerSize = 0;
                        item.dockPanel = {};
                        item.$target.find('>[data-dock-panel]').each(function () {

                            var panelInfo = {};
                            (function (data) {
                                if (U.isObject(data) && !data.error) {
                                    panelInfo = jQuery.extend(true, panelInfo, data);
                                }
                            })(U.parseJson(this.getAttribute("data-dock-panel"), true));

                            if ('dock' in panelInfo) {
                                panelInfo.$target = jQuery(this);
                                panelInfo.$target.addClass("dock-panel-" + panelInfo.dock);

                                if (panelInfo.split && panelInfo.split.toString() == "true") {
                                    panelInfo.$splitter = jQuery('

');
                                    panelInfo.$splitter
                                        .bind(ENM["mousedown"], function (e) {
                                            // console.log(e.clientX);
                                            panelInfo.mousePosition = getMousePosition(e);
                                            resizeSplitter.on.call(self, queIdx, panelInfo);
                                        })
                                        .bind("dragstart", function (e) {
                                            U.stopEvent(e);
                                            return false;
                                        });
                                    panelInfo.$target.append(panelInfo.$splitter);
                                    outerSize = cfg.splitter.size;
                                }

                                if (panelInfo.dock == "top" || panelInfo.dock == "bottom") {
                                    panelInfo.__height = panelInfo.height + outerSize;
                                }
                                else {
                                    panelInfo.__width = panelInfo.width + outerSize;
                                }

                                item.dockPanel[panelInfo.dock] = panelInfo;
                            }
                        });

                    }
                };

                return function (queIdx) {
                    var item = this.queue[queIdx];
                    var data = {};

                    // 부모 컨테이너가 ax5layout인지 판단 필요.
                    if (item.$target.parents("[data-ax5layout]").get(0)) {
                        hooksResizeLayout.call(
                            this,
                            item.$target.parents("[data-ax5layout]").get(0),
                            queIdx
                        );
                    }

                    if (item.control in applyLayout) {
                        applyLayout[item.control].call(this, queIdx);
                    }
                    alignLayout.call(this, queIdx);
                    //item.$target.find();
                }
            })(),
            getQueIdx = function (boundID) {
                if (!U.isString(boundID)) {
                    boundID = jQuery(boundID).data("data-ax5layout-id");
                }
                if (!U.isString(boundID)) {
                    console.log(ax5.info.getError("ax5layout", "402", "getQueIdx"));
                    return;
                }
                return U.search(this.queue, function () {
                    return this.id == boundID;
                });
            },
            hooksResizeLayout = function (boundID, childQueIdx) {
                var queIdx = (U.isNumber(boundID)) ? boundID : getQueIdx.call(this, boundID);
                if (!this.queue[queIdx].childQueIdxs) this.queue[queIdx].childQueIdxs = [];
                this.queue[queIdx].childQueIdxs.push(childQueIdx);
                this.queue[childQueIdx].parentQueIdx = queIdx;
            };
        /// private end

        /**
         * Preferences of layout UI
         * @method ax5.ui.layout.setConfig
         * @param {Object} config - 클래스 속성값
         * @returns {ax5.ui.layout}
         * @example
         * ```
         * ```
         */
        this.init = function () {
            this.onStateChanged = cfg.onStateChanged;
            this.onClick = cfg.onClick;
            jQuery(window).bind("resize.ax5layout-" + this.instanceId, (function () {
                alignLayoutAll.call(this);
                /*
                 setTimeout((function(){
                 alignLayoutAll.call(this);
                 }).bind(this), 100);
                 */
            }).bind(this));
        };

        /**
         * ax5.ui.layout.bind
         * @param {Object} item
         * @param {String} [item.control]
         * @param {String} [item.theme]
         * @param {Element} item.target
         * @param {Object[]} item.options
         * @returns {ax5.ui.layout}
         */
        this.bind = function (item) {
            var
                UIConfig = {},
                queIdx;

            item = jQuery.extend(true, UIConfig, cfg, item);
            if (!item.target) {
                console.log(ax5.info.getError("ax5layout", "401", "bind"));
                return this;
            }

            item.$target = jQuery(item.target);

            if (!item.id) item.id = item.$target.data("data-ax5layout-id");
            if (!item.id) {
                item.id = 'ax5layout-' + ax5.getGuid();
                item.$target.data("data-ax5layout-id", item.id);
            }
            item.name = item.$target.attr("data-ax5layout");
            if (item.options) {
                item.options = JSON.parse(JSON.stringify(item.options));
            }

            // target attribute data
            (function (data) {
                if (U.isObject(data) && !data.error) {
                    item = jQuery.extend(true, item, data);
                }
            })(U.parseJson(item.$target.attr("data-config"), true));

            queIdx = U.search(this.queue, function () {
                return this.id == item.id;
            });

            if (queIdx === -1) {
                this.queue.push(item);
                bindLayoutTarget.call(this, this.queue.length - 1);
            }
            else {
                this.queue[queIdx] = jQuery.extend(true, {}, this.queue[queIdx], item);
                bindLayoutTarget.call(this, queIdx);
            }

            UIConfig = null;
            queIdx = null;
            return this;
        };

        /**
         * ax5.ui.layout.resize
         * @param boundID
         * @param {Function} [callBack]
         * @returns {ax5.ui.layout}
         */
        this.resize = function (boundID, callBack) {
            var queIdx = (U.isNumber(boundID)) ? boundID : getQueIdx.call(this, boundID);
            if (queIdx === -1) {
                console.log(ax5.info.getError("ax5layout", "402", "resize"));
                return;
            }
            alignLayout.call(this, queIdx, callBack);
            return this;
        };

        /**
         * ax5.ui.layout.onResize
         * @param boundID
         * @param fn
         * @returns {ax5.ui.layout}
         */
        this.onResize = function (boundID, fn) {
            var queIdx = (U.isNumber(boundID)) ? boundID : getQueIdx.call(this, boundID);
            if (queIdx === -1) {
                console.log(ax5.info.getError("ax5layout", "402", "onResize"));
                return;
            }
            this.queue[queIdx].onResize = fn;
            return this;
        };

        // 클래스 생성자
        this.main = (function () {
            if (arguments && U.isObject(arguments[0])) {
                this.setConfig(arguments[0]);
            }
            else {
                this.init();
            }
        }).apply(this, arguments);
    };
    //== UI Class

    root.layout = (function () {
        if (U.isFunction(_SUPER_)) axClass.prototype = new _SUPER_(); // 상속
        return axClass;
    })(); // ax5.ui에 연결

})(ax5.ui, ax5.ui.root);

ax5.ui.layout_instance = new ax5.ui.layout();
jQuery.fn.ax5layout = (function () {
    return function (config) {
        if (ax5.util.isString(arguments[0])) {
            var methodName = arguments[0];

            switch (methodName) {
                case "resize":
                    return ax5.ui.layout_instance.resize(this);
                    break;
                case "onResize":
                    return ax5.ui.layout_instance.onResize(this, arguments[1]);
                    break;

                default:
                    return this;
            }
        }
        else {
            if (typeof config == "undefined") config = {};
            jQuery.each(this, function () {
                var defaultConfig = {
                    target: this
                };
                config = jQuery.extend({}, config, defaultConfig);
                ax5.ui.layout_instance.bind(config);
            });
        }
        return this;
    }
})();<commit_msg>layout __da<commit_after>/*
 * Copyright (c) 2016. tom@axisj.com
 * - github.com/thomasjang
 * - www.axisj.com
 */

// ax5.ui.layout
(function (root, _SUPER_) {

    /**
     * @class ax5.ui.layout
     * @classdesc
     * @version 0.1.0
     * @author tom@axisj.com
     * @example
     * ```
     * var myLayout = new ax5.ui.layout();
     * ```
     */
    var U = ax5.util;

    //== UI Class
    var axClass = function () {
        var
            self = this,
            cfg,
            ENM = {
                "mousedown": (ax5.info.supportTouch) ? "touchstart" : "mousedown",
                "mousemove": (ax5.info.supportTouch) ? "touchmove" : "mousemove",
                "mouseup": (ax5.info.supportTouch) ? "touchend" : "mouseup"
            },
            getMousePosition = function (e) {
                var mouseObj = e;
                if ('changedTouches' in e) {
                    mouseObj = e.changedTouches[0];
                }
                return {
                    clientX: mouseObj.clientX,
                    clientY: mouseObj.clientY
                }
            };

        if (_SUPER_) _SUPER_.call(this); // 부모호출

        this.queue = [];
        this.config = {
            theme: 'default',
            animateTime: 250,
            splitter: {
                size: 5
            }
        };

        this.openTimer = null;
        this.closeTimer = null;
        this.resizer = null;

        cfg = this.config;

        var
            onStateChanged = function (opts, that) {
                if (opts && opts.onStateChanged) {
                    opts.onStateChanged.call(that, that);
                }
                else if (this.onStateChanged) {
                    this.onStateChanged.call(that, that);
                }
                return true;
            },
            alignLayoutAll = function () {
                var i = this.queue.length;
                while (i--) {
                    if (typeof this.queue[i].parentQueIdx === "undefined") alignLayout.call(this, i);
                }
            },
            alignLayout = (function () {

                var setCSS = {
                    'top': function (item, panel) {
                        panel.$target.css({height: panel.__height || 0});
                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({height: cfg.splitter.size});
                        }
                    },
                    'bottom': function (item, panel) {
                        panel.$target.css({height: panel.__height || 0});
                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({height: cfg.splitter.size});
                        }
                    },
                    'left': function (item, panel) {
                        var css = {
                            width: panel.__width,
                            height: item.targetDimension.height
                        };

                        if (item.dockPanel.top) {
                            css.height -= item.dockPanel.top.__height;
                            css.top = item.dockPanel.top.__height;
                        }
                        if (item.dockPanel.bottom) {
                            css.height -= item.dockPanel.bottom.__height;
                        }

                        panel.$target.css(css);

                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({width: cfg.splitter.size});
                        }
                    },
                    'right': function (item, panel) {
                        var css = {
                            width: panel.__width,
                            height: item.targetDimension.height
                        };

                        if (item.dockPanel.top) {
                            css.height -= item.dockPanel.top.__height;
                            css.top = item.dockPanel.top.__height;
                        }
                        if (item.dockPanel.bottom) {
                            css.height -= item.dockPanel.bottom.__height;
                        }

                        panel.$target.css(css);

                        if (panel.split && panel.split.toString() == "true") {
                            panel.$splitter.css({width: cfg.splitter.size});
                        }
                    },
                    'center': function (item, panel) {
                        var css = {
                            width: item.targetDimension.width,
                            height: item.targetDimension.height
                        };

                        if (item.dockPanel.top) {
                            css.height -= item.dockPanel.top.__height || 0;
                            css.top = item.dockPanel.top.__height || 0;
                        }
                        if (item.dockPanel.bottom) {
                            css.height -= item.dockPanel.bottom.__height || 0;
                        }
                        if (item.dockPanel.left) {
                            css.width -= item.dockPanel.left.__width || 0;
                            css.left = item.dockPanel.left.__width || 0;
                        }
                        if (item.dockPanel.right) {
                            css.width -= item.dockPanel.right.__width || 0;
                        }

                        panel.$target.css(css);
                    }
                };
                var childResize = function (item) {
                    var i = item.childQueIdxs.length;
                    while (i--) {
                        alignLayout.call(this, item.childQueIdxs[i]);
                    }
                };

                return function (queIdx, callBack) {

                    var item = this.queue[queIdx];

                    // 레이아웃 타겟의 CSS속성을 미리 저장해 둡니다. 왜? 패널별로 크기 계산 할 때 쓰려고
                    item.targetDimension = {
                        height: item.$target.innerHeight(),
                        width: item.$target.innerWidth()
                    };

                    // dockPanel
                    if (item.control == "dock-panel") {
                        for (var panel in item.dockPanel) {
                            if (item.dockPanel[panel].$target && item.dockPanel[panel].$target.get(0)) {
                                if (panel in setCSS) {
                                    setCSS[panel].call(this, item, item.dockPanel[panel]);
                                }
                            }
                        }
                    }

                    if (item.childQueIdxs) childResize.call(this, item);
                    if (item.onResize) {
                        setTimeout((function () {
                            this.onResize.call(this, this);
                        }).bind(item), 1)

                    }
                    if (callBack) {
                        callBack.call(item, item);
                    }
                }
            })(),
            bindLayoutTarget = (function () {

                var resizeSplitter = {
                    on: function (queIdx, panel) {

                        var splitterOffset = panel.$splitter.offset();
                        var splitterBox = {
                            width: panel.$splitter.width(), height: panel.$splitter.height()
                        };
                        var getResizerPosition = {
                            'left': function (e) {
                                panel.__da = e.clientX - panel.mousePosition.clientX;
                                return {left: panel.$splitter.offset().left + e.clientX - panel.mousePosition.clientX};
                            }
                        };
                        panel.__da = 0; // 패널의 변화량

                        jQuery(document.body)
                            .bind(ENM["mousemove"] + ".ax5layout-" + this.instanceId, function (e) {
                                // console.log(e.clientX - panel.mousePosition.clientX);
                                if (!self.resizer) {
                                    self.resizer = jQuery('

');
                                    self.resizer.css({
                                        left: splitterOffset.left,
                                        top: splitterOffset.top,
                                        width: splitterBox.width,
                                        height: splitterBox.height
                                    });
                                    jQuery(document.body).append(self.resizer);
                                }
                                self.resizer.css(getResizerPosition[panel.dock](e));
                            })
                            .bind(ENM["mouseup"] + ".ax5layout-" + this.instanceId, function (e) {
                                resizeSplitter.off.call(self, queIdx, panel);
                            })
                            .bind("mouseleave.ax5layout-" + this.instanceId, function (e) {
                                resizeSplitter.off.call(self, queIdx, panel);
                            });

                    },
                    off: function (queIdx, panel) {

                        if(self.resizer) {
                            self.resizer.remove();
                            self.resizer = null;
                        }

                        jQuery(document.body)
                            .unbind(ENM["mousemove"] + ".ax5layout-" + this.instanceId)
                            .unbind(ENM["mouseup"] + ".ax5layout-" + this.instanceId)
                            .unbind("mouseleave.ax5layout-" + this.instanceId);

                    }
                };

                var applyLayout = {
                    'dock-panel': function (queIdx) {
                        var item = this.queue[queIdx], outerSize = 0;
                        item.dockPanel = {};
                        item.$target.find('>[data-dock-panel]').each(function () {

                            var panelInfo = {};
                            (function (data) {
                                if (U.isObject(data) && !data.error) {
                                    panelInfo = jQuery.extend(true, panelInfo, data);
                                }
                            })(U.parseJson(this.getAttribute("data-dock-panel"), true));

                            if ('dock' in panelInfo) {
                                panelInfo.$target = jQuery(this);
                                panelInfo.$target.addClass("dock-panel-" + panelInfo.dock);

                                if (panelInfo.split && panelInfo.split.toString() == "true") {
                                    panelInfo.$splitter = jQuery('

');
                                    panelInfo.$splitter
                                        .bind(ENM["mousedown"], function (e) {
                                            // console.log(e.clientX);
                                            panelInfo.mousePosition = getMousePosition(e);
                                            resizeSplitter.on.call(self, queIdx, panelInfo);
                                        })
                                        .bind("dragstart", function (e) {
                                            U.stopEvent(e);
                                            return false;
                                        });
                                    panelInfo.$target.append(panelInfo.$splitter);
                                    outerSize = cfg.splitter.size;
                                }

                                if (panelInfo.dock == "top" || panelInfo.dock == "bottom") {
                                    panelInfo.__height = panelInfo.height + outerSize;
                                }
                                else {
                                    panelInfo.__width = panelInfo.width + outerSize;
                                }

                                item.dockPanel[panelInfo.dock] = panelInfo;
                            }
                        });

                    }
                };

                return function (queIdx) {
                    var item = this.queue[queIdx];
                    var data = {};

                    // 부모 컨테이너가 ax5layout인지 판단 필요.
                    if (item.$target.parents("[data-ax5layout]").get(0)) {
                        hooksResizeLayout.call(
                            this,
                            item.$target.parents("[data-ax5layout]").get(0),
                            queIdx
                        );
                    }

                    if (item.control in applyLayout) {
                        applyLayout[item.control].call(this, queIdx);
                    }
                    alignLayout.call(this, queIdx);
                    //item.$target.find();
                }
            })(),
            getQueIdx = function (boundID) {
                if (!U.isString(boundID)) {
                    boundID = jQuery(boundID).data("data-ax5layout-id");
                }
                if (!U.isString(boundID)) {
                    console.log(ax5.info.getError("ax5layout", "402", "getQueIdx"));
                    return;
                }
                return U.search(this.queue, function () {
                    return this.id == boundID;
                });
            },
            hooksResizeLayout = function (boundID, childQueIdx) {
                var queIdx = (U.isNumber(boundID)) ? boundID : getQueIdx.call(this, boundID);
                if (!this.queue[queIdx].childQueIdxs) this.queue[queIdx].childQueIdxs = [];
                this.queue[queIdx].childQueIdxs.push(childQueIdx);
                this.queue[childQueIdx].parentQueIdx = queIdx;
            };
        /// private end

        /**
         * Preferences of layout UI
         * @method ax5.ui.layout.setConfig
         * @param {Object} config - 클래스 속성값
         * @returns {ax5.ui.layout}
         * @example
         * ```
         * ```
         */
        this.init = function () {
            this.onStateChanged = cfg.onStateChanged;
            this.onClick = cfg.onClick;
            jQuery(window).bind("resize.ax5layout-" + this.instanceId, (function () {
                alignLayoutAll.call(this);
                /*
                 setTimeout((function(){
                 alignLayoutAll.call(this);
                 }).bind(this), 100);
                 */
            }).bind(this));
        };

        /**
         * ax5.ui.layout.bind
         * @param {Object} item
         * @param {String} [item.control]
         * @param {String} [item.theme]
         * @param {Element} item.target
         * @param {Object[]} item.options
         * @returns {ax5.ui.layout}
         */
        this.bind = function (item) {
            var
                UIConfig = {},
                queIdx;

            item = jQuery.extend(true, UIConfig, cfg, item);
            if (!item.target) {
                console.log(ax5.info.getError("ax5layout", "401", "bind"));
                return this;
            }

            item.$target = jQuery(item.target);

            if (!item.id) item.id = item.$target.data("data-ax5layout-id");
            if (!item.id) {
                item.id = 'ax5layout-' + ax5.getGuid();
                item.$target.data("data-ax5layout-id", item.id);
            }
            item.name = item.$target.attr("data-ax5layout");
            if (item.options) {
                item.options = JSON.parse(JSON.stringify(item.options));
            }

            // target attribute data
            (function (data) {
                if (U.isObject(data) && !data.error) {
                    item = jQuery.extend(true, item, data);
                }
            })(U.parseJson(item.$target.attr("data-config"), true));

            queIdx = U.search(this.queue, function () {
                return this.id == item.id;
            });

            if (queIdx === -1) {
                this.queue.push(item);
                bindLayoutTarget.call(this, this.queue.length - 1);
            }
            else {
                this.queue[queIdx] = jQuery.extend(true, {}, this.queue[queIdx], item);
                bindLayoutTarget.call(this, queIdx);
            }

            UIConfig = null;
            queIdx = null;
            return this;
        };

        /**
         * ax5.ui.layout.resize
         * @param boundID
         * @param {Function} [callBack]
         * @returns {ax5.ui.layout}
         */
        this.resize = function (boundID, callBack) {
            var queIdx = (U.isNumber(boundID)) ? boundID : getQueIdx.call(this, boundID);
            if (queIdx === -1) {
                console.log(ax5.info.getError("ax5layout", "402", "resize"));
                return;
            }
            alignLayout.call(this, queIdx, callBack);
            return this;
        };

        /**
         * ax5.ui.layout.onResize
         * @param boundID
         * @param fn
         * @returns {ax5.ui.layout}
         */
        this.onResize = function (boundID, fn) {
            var queIdx = (U.isNumber(boundID)) ? boundID : getQueIdx.call(this, boundID);
            if (queIdx === -1) {
                console.log(ax5.info.getError("ax5layout", "402", "onResize"));
                return;
            }
            this.queue[queIdx].onResize = fn;
            return this;
        };

        // 클래스 생성자
        this.main = (function () {
            if (arguments && U.isObject(arguments[0])) {
                this.setConfig(arguments[0]);
            }
            else {
                this.init();
            }
        }).apply(this, arguments);
    };
    //== UI Class

    root.layout = (function () {
        if (U.isFunction(_SUPER_)) axClass.prototype = new _SUPER_(); // 상속
        return axClass;
    })(); // ax5.ui에 연결

})(ax5.ui, ax5.ui.root);

ax5.ui.layout_instance = new ax5.ui.layout();
jQuery.fn.ax5layout = (function () {
    return function (config) {
        if (ax5.util.isString(arguments[0])) {
            var methodName = arguments[0];

            switch (methodName) {
                case "resize":
                    return ax5.ui.layout_instance.resize(this);
                    break;
                case "onResize":
                    return ax5.ui.layout_instance.onResize(this, arguments[1]);
                    break;

                default:
                    return this;
            }
        }
        else {
            if (typeof config == "undefined") config = {};
            jQuery.each(this, function () {
                var defaultConfig = {
                    target: this
                };
                config = jQuery.extend({}, config, defaultConfig);
                ax5.ui.layout_instance.bind(config);
            });
        }
        return this;
    }
})();<|endoftext|>


<commit_before>/**
@fileOverview People manager UI, including groups and person view.
*/

/**
@class
@extends $.boom.item
*/
$.boom.person = {};

$.extend($.boom.person, $.boom.item,
	/**
	@lends $.boom.person
	*/
	{

	base_url: '/cms/people/',

	type: 'person',

	/** @function */
	bind: function( context){

		var self = this;

		this.groups.person_id = this.rid;

		$.boom.dialog.bind({
			image: $('.boom-asset-preview', context )
		});

		$('.b-people-groups-add', context ).click(function(){

			self.groups.add();
		});

		$('.b-people-group-delete', context ).click(function(){
			var $this = $( this );
			var $li = $this.closest( 'li' );
			var group_id = $this.attr( 'rel' );

			self.groups.remove( group_id )
			.done( function(){
				$li.remove();
			});

		});

		$('.b-people-save', context ).bind('save', function( event ){

			var data = $( '#boom-person-view > form' ).serialize();

			self
				.save( data )
				.done( function(){
					$.boom.growl.show( "Person saved." );
				});

		}).click(function(){

			$( this ).trigger( 'save' );
		});

		$('#b-delete-person', context ).click(function( event ){

			self.remove()
				.done( function(){
					$.boom.history.load( 'group/0' );
				});

		});
	},

	/**
	Handle UI for adding and removing groups from a person
	@static
	@class
	*/
	groups: {

		/**
		@property person_id
		*/
		person_id: null,

		/** @function */
		add: function(){

			var self = this;

			var dialog = $.boom.dialog.open({
				url: '/cms/people/add_group/' + self.person_id,
				title: 'Add group',
				callback: function(){

					var dialog = this;
					var data = $( dialog ).find('form').serialize();
					$.boom.loader.show();

					return $.post('/cms/people/add_group/' + self.person_id, data )
					.done( function(){

						$.boom.loader.hide();
						$.boom.history
							.load( 'person/' + self.person_id )
							.done( function(){
								$( '#boom-person-view ul[role=tablist] a:eq(2)' ).click();
							});
					});
				}
			});
		},

		/** @function */
		remove: function( group_id ){

			var self = this;

			$.boom.loader.show();

			return $.post( '/cms/people/remove_group/' + self.person_id, {group_id: group_id} )
				.done( function(){
					$.boom.loader.hide();
				});
		}
	}
});

/**
Filter lists of people by group.
@class
@extends $.boom.filter
*/
$.boom.filter_people = $.extend( {}, $.boom.filter, {

	base_url: '/cms/people/',

	type: 'group'
});

/**
* User interface for browsing and managing people.
* @class
* @name $.boom.browser_people
* @extends $.boom.browser
*/
$.widget( 'boom.browser_people', $.boom.browser,
	/** @lends $.boom.browser_people */
	{

	/**
	@see $.boom.config.browser_people
	*/
	options: $.boom.config.browser_people,

	_create : function(){

		$.boom.log( 'people browser init' );

		var self = this;

		self.item = $.boom.person;
		self.tag = $.boom.filter_people;

		$.boom.browser.prototype._create.call( this );

	},

	_bind: function(){
		$.boom.log( 'people browser bind' );

		$.boom.browser.prototype._bind.call( this );

		var self = this;

		self.sidebar.group_editor({
			tree_config: self.editableTreeConfig,
			browser: self
		});

		$('#boom-tagmanager-create-person').click(function(){
			var dialog = $.boom.dialog.open({
				url: '/cms/people/add',
				title: 'Create new person',
				callback: function(){

					var data = $('#boom-tagmanager-create-person-form').serialize();

					$.boom.person.add( data )
						.done(function() {
							$.boom.growl.show('Success');
							$.boom.history.refresh();
						})
						.fail(function() {
							$.boom.growl.show('Failure');
						});
				}
			});
		});

		$('#b-topbar')
			.on('click', '#b-button-multiaction-edit', function(){

				var ids = [];

				$('.b-items-select-checkbox:checked').each(function(){

					var id = this.id.replace(/person-(thumb|list)-/, '');

					if ( $.inArray(id, ids) === -1 ) {

						ids.push( id );
					}
				});

				$.boom.history.load('person/' + ids.join('-'));
			})
			.on('click', '#b-button-multiaction-delete', function(){

				var msg = 'Are you sure you want to send the selected people to the rubbish bin?';

				$.boom.dialog
					.confirm('Confirm deletion', msg)
					.done( function(){

						var people = [];

						$('.b-items-select-checkbox:checked').each(function(i){

							people.push( $( this ).attr( 'id' ).replace(/person-(thumb|list)-/, '') );
						});

						$.boom.loader.show();

						$.post('/cms/people/delete', {people: people}, function(){

							$.boom.loader.hide();

							$.boom.history.refresh();
						});
					});
			});

		self.main_panel
			.on( 'change', '#boom-tagmanager-sortby-select', function( event ){
				self.tag.options.sortby = this.value;
				$.boom.history.refresh();
			})
			.on( 'mouseenter focus', '#b-items-view-list tbody tr, #b-items-view-thumbs a', function( event ){
				$( this ).addClass( 'ui-state-hover' );
			})
			.on( 'mouseleave blur', '#b-items-view-list tbody tr, #b-items-view-thumbs a', function( event ){
				$( this ).removeClass( 'ui-state-hover' );
			})
			.on( 'click', '.boom-pagination a', function( e ){
				e.preventDefault();

				//$.boom.history.load( '/cms/assets/list?' + $( this ).attr( 'href' ).split( '?' )[ 1 ] );
				$.get( '/cms/assets/list?' + $( this ).attr( 'href' ).split( '?' )[ 1 ])
				.done( function( data ){
					var $data = $( data );
					var pagination = $data.find( '.boom-pagination' ).html();
					var list = $data.find( '#b-items-view-list' ).html();
					var thumbs = $data.find( '#b-items-view-thumbs' ).html();
					$( self.main_panel )
						.find( '.boom-pagination' )
						.html( pagination )
						.end()
						.find( '#b-items-view-list' )
						.html( list )
						.end()
						.find( '#b-items-view-thumbs' )
						.html( thumbs )
						.end()
						.trigger('justify');
				});

				return false;
			});
	}
});
<commit_msg>Refactoring HTML / CSS<commit_after>/**
@fileOverview People manager UI, including groups and person view.
*/

/**
@class
@extends $.boom.item
*/
$.boom.person = {};

$.extend($.boom.person, $.boom.item,
	/**
	@lends $.boom.person
	*/
	{

	base_url: '/cms/people/',

	type: 'person',

	/** @function */
	bind: function( context){

		var self = this;

		this.groups.person_id = this.rid;

		$.boom.dialog.bind({
			image: $('.boom-asset-preview', context )
		});

		$('.b-people-groups-add', context ).click(function(){

			self.groups.add();
		});

		$('.b-people-group-delete', context ).click(function(){
			var $this = $( this );
			var $li = $this.closest( 'li' );
			var group_id = $this.attr( 'rel' );

			self.groups.remove( group_id )
			.done( function(){
				$li.remove();
			});

		});

		$('.b-people-save', context ).bind('save', function( event ){

			var data = $( '#boom-person-view > form' ).serialize();

			self
				.save( data )
				.done( function(){
					$.boom.growl.show( "Person saved." );
				});

		}).click(function(){

			$( this ).trigger( 'save' );
		});

		$('#b-delete-person', context ).click(function( event ){

			self.remove()
				.done( function(){
					$.boom.history.load( 'group/0' );
				});

		});
	},

	/**
	Handle UI for adding and removing groups from a person
	@static
	@class
	*/
	groups: {

		/**
		@property person_id
		*/
		person_id: null,

		/** @function */
		add: function(){

			var self = this;

			var dialog = $.boom.dialog.open({
				url: '/cms/people/add_group/' + self.person_id,
				title: 'Add group',
				callback: function(){

					var dialog = this;
					var data = $( dialog ).find('form').serialize();
					$.boom.loader.show();

					return $.post('/cms/people/add_group/' + self.person_id, data )
					.done( function(){

						$.boom.loader.hide();
						$.boom.history
							.load( 'person/' + self.person_id )
							.done( function(){
								$( '#boom-person-view ul[role=tablist] a:eq(2)' ).click();
							});
					});
				}
			});
		},

		/** @function */
		remove: function( group_id ){

			var self = this;

			$.boom.loader.show();

			return $.post( '/cms/people/remove_group/' + self.person_id, {group_id: group_id} )
				.done( function(){
					$.boom.loader.hide();
				});
		}
	}
});

/**
Filter lists of people by group.
@class
@extends $.boom.filter
*/
$.boom.filter_people = $.extend( {}, $.boom.filter, {

	base_url: '/cms/people/',

	type: 'group'
});

/**
* User interface for browsing and managing people.
* @class
* @name $.boom.browser_people
* @extends $.boom.browser
*/
$.widget( 'boom.browser_people', $.boom.browser,
	/** @lends $.boom.browser_people */
	{

	/**
	@see $.boom.config.browser_people
	*/
	options: $.boom.config.browser_people,

	_create : function(){

		$.boom.log( 'people browser init' );

		var self = this;

		self.item = $.boom.person;
		self.tag = $.boom.filter_people;

		$.boom.browser.prototype._create.call( this );

	},

	_bind: function(){
		$.boom.log( 'people browser bind' );

		$.boom.browser.prototype._bind.call( this );

		var self = this;

		self.treeConfig = $.extend({}, $.boom.config.tree, {
			toggleSelected: false,
			click: false,
			onClick: function(event){
				$this = $(this);

				var tags = multi_select( $this );

				self.tag.set_filters( tags );

				$.boom.history.load( tag_name + '/' + self.tag.rid );
				return false;
			}

		});

		$( '.boom-filter-tree' )
			.tree( self.treeConfig );

		self.editableTreeConfig = $.extend({}, self.treeConfig, {
			maxSelected: 1,
			toggleSelected: false,
			preventDefault: true,
			onClick: function(event){
				$this = $(this);
				item_selected( $this );

				self.tag.rid =
					$this
						.attr( 'href' )
						.split('/')
						[1];

				$.boom.history.load( tag_name + '/' + self.tag.rid );
				return false;
			}
		});

		var item_selected = function( $item ){
			$( '#tag_all' ).removeClass( 'ui-state-active' );

			$.boom.log( 'adding active class' );
			$item
				.parents( '.b-tags-tree' )
				.find( 'a.ui-state-active' )
				.removeClass( 'ui-state-active' )
				.end()
				.end()
				.addClass( 'ui-state-active' );
		};

		var tag_name  = self.options.defaultRoute.split( '/' )[ 0 ];

		self.sidebar.group_editor({
			tree_config: self.editableTreeConfig,
			browser: self
		});

		$('#boom-tagmanager-create-person').click(function(){
			var dialog = $.boom.dialog.open({
				url: '/cms/people/add',
				title: 'Create new person',
				callback: function(){

					var data = $('#boom-tagmanager-create-person-form').serialize();

					$.boom.person.add( data )
						.done(function() {
							$.boom.growl.show('Success');
							$.boom.history.refresh();
						})
						.fail(function() {
							$.boom.growl.show('Failure');
						});
				}
			});
		});

		$('#b-topbar')
			.on('click', '#b-button-multiaction-edit', function(){

				var ids = [];

				$('.b-items-select-checkbox:checked').each(function(){

					var id = this.id.replace(/person-(thumb|list)-/, '');

					if ( $.inArray(id, ids) === -1 ) {

						ids.push( id );
					}
				});

				$.boom.history.load('person/' + ids.join('-'));
			})
			.on('click', '#b-button-multiaction-delete', function(){

				var msg = 'Are you sure you want to send the selected people to the rubbish bin?';

				$.boom.dialog
					.confirm('Confirm deletion', msg)
					.done( function(){

						var people = [];

						$('.b-items-select-checkbox:checked').each(function(i){

							people.push( $( this ).attr( 'id' ).replace(/person-(thumb|list)-/, '') );
						});

						$.boom.loader.show();

						$.post('/cms/people/delete', {people: people}, function(){

							$.boom.loader.hide();

							$.boom.history.refresh();
						});
					});
			});

		self.main_panel
			.on( 'change', '#boom-tagmanager-sortby-select', function( event ){
				self.tag.options.sortby = this.value;
				$.boom.history.refresh();
			})
			.on( 'mouseenter focus', '#b-items-view-list tbody tr, #b-items-view-thumbs a', function( event ){
				$( this ).addClass( 'ui-state-hover' );
			})
			.on( 'mouseleave blur', '#b-items-view-list tbody tr, #b-items-view-thumbs a', function( event ){
				$( this ).removeClass( 'ui-state-hover' );
			})
			.on( 'click', '.boom-pagination a', function( e ){
				e.preventDefault();

				//$.boom.history.load( '/cms/assets/list?' + $( this ).attr( 'href' ).split( '?' )[ 1 ] );
				$.get( '/cms/assets/list?' + $( this ).attr( 'href' ).split( '?' )[ 1 ])
				.done( function( data ){
					var $data = $( data );
					var pagination = $data.find( '.boom-pagination' ).html();
					var list = $data.find( '#b-items-view-list' ).html();
					var thumbs = $data.find( '#b-items-view-thumbs' ).html();
					$( self.main_panel )
						.find( '.boom-pagination' )
						.html( pagination )
						.end()
						.find( '#b-items-view-list' )
						.html( list )
						.end()
						.find( '#b-items-view-thumbs' )
						.html( thumbs )
						.end()
						.trigger('justify');
				});

				return false;
			});
	}
});
<|endoftext|>


<commit_before>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('unisubs.widget.WidgetController');

/**
 * @constructor
 *
 */
unisubs.widget.WidgetController = function(videoURL, videoPlayer, videoTab) {
    // TODO: when all VideoSource implementations support getVideoURL,
    // remove videoURL from the parameters for this constructor.
    this.videoURL_ = videoURL;
    this.videoPlayer_ = videoPlayer;
    this.videoTab_ = videoTab;
};

/**
 * Widget calls this when show_widget rpc call returns.
 */
unisubs.widget.WidgetController.prototype.initializeState = function(result) {
//    try {
        this.initializeStateImpl_(result);
/*    }
    catch (e) {
        this.videoTab_.showError();
    }
*/
};

unisubs.widget.WidgetController.prototype.initializeStateImpl_ = function(result) {
    unisubs.widget.WidgetController.makeGeneralSettings(result);

    var videoID = result['video_id'];

    if (videoID){
        this.videoTab_.createShareButton(
            new goog.Uri(unisubs.getSubtitleHomepageURL(videoID)), false);
    }

    var dropDownContents = new unisubs.widget.DropDownContents(
        result['drop_down_contents'],
        result['my_languages'],
        result["is_moderated"]);
    var subtitleState = unisubs.widget.SubtitleState.fromJSON(
        result['subtitles']);

    var popupMenu = new unisubs.widget.DropDown(
        videoID, dropDownContents, this.videoTab_);

    this.videoTab_.showContent(popupMenu.hasSubtitles(),
                               subtitleState);

    popupMenu.render();
    unisubs.style.showElement(popupMenu.getElement(), false);

    popupMenu.setCurrentSubtitleState(subtitleState);
    popupMenu.dispatchLanguageSelection_(null);

    this.playController_ = new unisubs.widget.PlayController(
        videoID, this.videoPlayer_.getVideoSource(), this.videoPlayer_,
        this.videoTab_, popupMenu, subtitleState);

    this.subtitleController_ = new unisubs.widget.SubtitleController(
        videoID, this.videoURL_,
        this.playController_, this.videoTab_, popupMenu);
};

/**
 * Sets parameters that the widget needs to function: username,
 * embed version, writelock expiration, languages, and metadata languages.
 * @param {Object} settings
 */
unisubs.widget.WidgetController.makeGeneralSettings = function(settings) {
    if (settings['username'])
        unisubs.currentUsername = settings['username'];
    unisubs.embedVersion = settings['embed_version'];
    unisubs.LOCK_EXPIRATION =
        settings["writelock_expiration"];
    unisubs.languages = settings['languages'];
    unisubs.metadataLanguages = settings['metadata_languages'];
    var sortFn = function(a, b) {
        return a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0
    };
    goog.array.sort(unisubs.languages, sortFn);
    goog.array.sort(unisubs.metadataLanguages, sortFn);
};

unisubs.widget.WidgetController.prototype.getSubtitleController = function() {
    return this.subtitleController_;
};

unisubs.widget.WidgetController.prototype.getPlayController = function() {
    return this.playController_;
};

unisubs.widget.WidgetController.prototype.openMenu = function(){
    this.subtitleController_.dropDown_.show();
}
<commit_msg>Uncommented try-catch, which was originally commented out for debugging.<commit_after>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('unisubs.widget.WidgetController');

/**
 * @constructor
 *
 */
unisubs.widget.WidgetController = function(videoURL, videoPlayer, videoTab) {
    // TODO: when all VideoSource implementations support getVideoURL,
    // remove videoURL from the parameters for this constructor.
    this.videoURL_ = videoURL;
    this.videoPlayer_ = videoPlayer;
    this.videoTab_ = videoTab;
};

/**
 * Widget calls this when show_widget rpc call returns.
 */
unisubs.widget.WidgetController.prototype.initializeState = function(result) {
    try {
        this.initializeStateImpl_(result);
    }
    catch (e) {
        this.videoTab_.showError();
    }
};

unisubs.widget.WidgetController.prototype.initializeStateImpl_ = function(result) {
    unisubs.widget.WidgetController.makeGeneralSettings(result);

    var videoID = result['video_id'];

    if (videoID){
        this.videoTab_.createShareButton(
            new goog.Uri(unisubs.getSubtitleHomepageURL(videoID)), false);
    }

    var dropDownContents = new unisubs.widget.DropDownContents(
        result['drop_down_contents'],
        result['my_languages'],
        result["is_moderated"]);
    var subtitleState = unisubs.widget.SubtitleState.fromJSON(
        result['subtitles']);

    var popupMenu = new unisubs.widget.DropDown(
        videoID, dropDownContents, this.videoTab_);

    this.videoTab_.showContent(popupMenu.hasSubtitles(),
                               subtitleState);

    popupMenu.render();
    unisubs.style.showElement(popupMenu.getElement(), false);

    popupMenu.setCurrentSubtitleState(subtitleState);
    popupMenu.dispatchLanguageSelection_(null);

    this.playController_ = new unisubs.widget.PlayController(
        videoID, this.videoPlayer_.getVideoSource(), this.videoPlayer_,
        this.videoTab_, popupMenu, subtitleState);

    this.subtitleController_ = new unisubs.widget.SubtitleController(
        videoID, this.videoURL_,
        this.playController_, this.videoTab_, popupMenu);
};

/**
 * Sets parameters that the widget needs to function: username,
 * embed version, writelock expiration, languages, and metadata languages.
 * @param {Object} settings
 */
unisubs.widget.WidgetController.makeGeneralSettings = function(settings) {
    if (settings['username'])
        unisubs.currentUsername = settings['username'];
    unisubs.embedVersion = settings['embed_version'];
    unisubs.LOCK_EXPIRATION =
        settings["writelock_expiration"];
    unisubs.languages = settings['languages'];
    unisubs.metadataLanguages = settings['metadata_languages'];
    var sortFn = function(a, b) {
        return a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0
    };
    goog.array.sort(unisubs.languages, sortFn);
    goog.array.sort(unisubs.metadataLanguages, sortFn);
};

unisubs.widget.WidgetController.prototype.getSubtitleController = function() {
    return this.subtitleController_;
};

unisubs.widget.WidgetController.prototype.getPlayController = function() {
    return this.playController_;
};

unisubs.widget.WidgetController.prototype.openMenu = function(){
    this.subtitleController_.dropDown_.show();
}
<|endoftext|>


<commit_before>/*    Copyright 2016-2019 Firewalla INC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program 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 Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict'

let log = require('../net2/logger.js')(__filename, 'info');
let ip = require('ip')

const minimatch = require('minimatch')

const _ = require('lodash')

const validator = require('validator');

const CategoryMatcher = require('./CategoryMatcher');

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length != b.length) return false;

  // If you don't care about the order of the elements inside
  // the array, you should sort both arrays here.

  for (var i = 0; i < a.length; ++i) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}

function isJsonString(str) {
  return _.isString(str) && validator.isJSON(str)
}

module.exports = class {
  constructor(raw) {
    const numberKeys = ["timestamp", "expireTs", "idleTs"]
    for (const key in raw) {
      if (isJsonString(raw[key])) {
        // parse will always be successful if passed check
        raw[key] = JSON.parse(raw[key])
      }
      if (numberKeys.includes(key))
        raw[key] = !isNaN(raw[key]) && Number(raw[key]);
      if (key == 'p.tag.ids' && Array.isArray(raw[key])) {
        raw[key] = raw[key].map(String) //Backward compatibility
      }
      if ((/^p\.tag\.ids\.[0-9]+$/).test(key)) {
        log.debug('legacy field found', key, raw[key])
        this['p.tag.ids'] = this['p.tag.ids'] || []
        this['p.tag.ids'].push(raw[key])
        delete raw[key]
      }
    }
    Object.assign(this, raw);

    if (!this.timestamp) this.timestamp = new Date() / 1000;
  }

  getMatchingKeys() {
    let keys = ["cronTime", "duration", "expireTs", "idleTs"]
    for (let k in this) {
      if (k === "type" || k.startsWith("p.") || k.startsWith("e.")) {
        keys.push(k)
      }
    }

    return keys.sort()
  }

  isEqualToException(e) {
    const thisKeys = this.getMatchingKeys()
    const thatKeys = e.getMatchingKeys()

    if (!arraysEqual(thisKeys, thatKeys)) {
      return false
    }
    for (let i in thisKeys) {
      let k = thisKeys[i]
      if (this[k] !== e[k]) {
        return false
      }
    }

    return true
  }

  isSecurityAlarm(alarm) {
    const securityAlarmTypes = [
      "ALARM_SPOOFING_DEVICE",
      "ALARM_BRO_NOTICE",
      "ALARM_INTEL",
      "ALARM_VULNERABILITY"
    ];

    return securityAlarmTypes.includes(alarm.type);
  }

  jsonComparisonMatch(val, val2) {
    if (!isFinite(val2)) return false;
    let comparison = JSON.parse(val);
    if (isFinite(comparison["$gt"])) {
      if (val2 > comparison["$gt"]) {
        return true;
      }
    } else if (isFinite(comparison["$lt"])) {
      if (val2 < comparison["$lt"]) {
        return true;
      }
    } else if (isFinite(comparison["$gte"])) {
      if (val2 > comparison["$gte"] || val2 == comparison["$gte"]) {
        return true;
      }
    } else if (isFinite(comparison["$lte"])) {
      if (val2 < comparison["$lte"] || val2 == comparison["$lte"]) {
        return true;
      }
    }

    return false;
  }

  valueMatch(val, val2) {
    //special exception
    if (val.endsWith("*")) {
      if (minimatch(val2, val)) {
        return true
      }
    }

    if (val.startsWith("*.")) {
      // use glob matching
      if (!minimatch(val2, val) && // NOT glob match
        val.slice(2) !== val2) { // NOT exact sub domain match
        return false
      }
    } else {
      let cidrParts = val.split("/", 2);
      if (cidrParts.length == 2) {
        let addr = cidrParts[0];
        let mask = cidrParts[1];
        if (ip.isV4Format(addr) && RegExp("^\\d+$").test(mask) && ip.isV4Format(val2)) {
          // try matching cidr subnet iff value in alarm is an ipv4 address and value in exception is a cidr notation
          if (!ip.cidrSubnet(val).contains(val2)) {
            return false;
          }
        }
      } else {
        // not a cidr subnet exception

        // alarm might has field in number
        // and assume exceptions are always loaded from redis before comparing
        if (_.isNumber(val2)) {
          val = _.toNumber(val)
          if (isNaN(val)) return false;
        }
        if (val2 !== val) return false;
      }
    }

    return true;
  }

  getCategory() {
    return this["p.category.id"];
  }

  setCategoryMatcher(matcher) {
    this.categoryMatcher = matcher;
  }

  isExpired() {
    if (this.expireTs)
      return Date.now() / 1000 > this.expireTs;
    else
      return false;
  }

  isIdle() {
    if (this.idleTs)
      return Date.now() / 1000 < this.idleTs;
    else
      return false;
  }

  match(alarm) {
    try {
      let matched = false;

      for (const key in this) {

        if (!key.startsWith("p.") && key !== "type" && !key.startsWith("e.")) {
          continue;
        }

        if (key === "p.category.id") {
          continue;
        }

        var val = this[key];
        if (!alarm[key]) return false;
        let val2 = alarm[key];

        if (key === "type" && val === "ALARM_INTEL" && this.isSecurityAlarm(alarm)) {
          matched = true;
          continue;
        }

        if ((this["json." + key] == true || this["json." + key] == "true") && val && validator.isJSON(val)) {
          if (this.jsonComparisonMatch(val, val2)) {
            matched = true;
            continue;
          }
        }
        let val2Array = val2;
        // Exception will be parsed at object creation
        // while alarm will always use string to avoid compatibility issue with clients
        isJsonString(val2) && (val2Array = JSON.parse(val2));

        if (key.startsWith("p.tag.ids")) {
          if (_.intersection(val, val2Array.map(String)).length > 0) {
            matched = true;
            continue;
          }
        }

        if (_.isArray(val)) {
          let matchInArray = false;
          for (const valCurrent of val) {
            if (this.valueMatch(valCurrent + "", val2)) {
              matchInArray = true;
              break;
            }
          }

          if (!matchInArray) {
            return false;
          }
        } else {
          if (!this.valueMatch(val, val2)) {
            return false;
          }
        }

        matched = true;
      }

      // match against category
      if (this.categoryMatcher) {
        if (this.getCategory() === alarm["p.dest.category"]) {
          // shortcut for direct category id match
          matched = true;
        } else {
          const targetDomain = alarm["p.dest.name"];
          const targetIP = alarm["p.dest.ip"];
          log.info(`Match dest domain ${targetDomain} and ip ${targetIP} against category ${this.getCategory()}`);
          if (targetDomain && this.categoryMatcher.matchDomain(targetDomain)) {
            matched = true;
          } else if (targetIP && this.categoryMatcher.matchIP(targetIP)) {
            matched = true;
          } else {
            return false;
          }
        }
      }

      return matched;
    } catch (e) {
      log.warn("Error on alarm matching", e);
      return false;
    }
  }
}
<commit_msg>use lodash's isEqual to compare keys in Exception.isEqualToException<commit_after>/*    Copyright 2016-2019 Firewalla INC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program 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 Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict'

let log = require('../net2/logger.js')(__filename, 'info');
let ip = require('ip')

const minimatch = require('minimatch')

const _ = require('lodash')

const validator = require('validator');

const CategoryMatcher = require('./CategoryMatcher');

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length != b.length) return false;

  // If you don't care about the order of the elements inside
  // the array, you should sort both arrays here.

  for (var i = 0; i < a.length; ++i) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}

function isJsonString(str) {
  return _.isString(str) && validator.isJSON(str)
}

module.exports = class {
  constructor(raw) {
    const numberKeys = ["timestamp", "expireTs", "idleTs"]
    for (const key in raw) {
      if (isJsonString(raw[key])) {
        // parse will always be successful if passed check
        raw[key] = JSON.parse(raw[key])
      }
      if (numberKeys.includes(key))
        raw[key] = !isNaN(raw[key]) && Number(raw[key]);
      if (key == 'p.tag.ids' && Array.isArray(raw[key])) {
        raw[key] = raw[key].map(String) //Backward compatibility
      }
      if ((/^p\.tag\.ids\.[0-9]+$/).test(key)) {
        log.debug('legacy field found', key, raw[key])
        this['p.tag.ids'] = this['p.tag.ids'] || []
        this['p.tag.ids'].push(raw[key])
        delete raw[key]
      }
    }
    Object.assign(this, raw);

    if (!this.timestamp) this.timestamp = new Date() / 1000;
  }

  getMatchingKeys() {
    let keys = ["cronTime", "duration", "expireTs", "idleTs"]
    for (let k in this) {
      if (k === "type" || k.startsWith("p.") || k.startsWith("e.")) {
        keys.push(k)
      }
    }

    return keys.sort()
  }

  isEqualToException(e) {
    const thisKeys = this.getMatchingKeys()
    const thatKeys = e.getMatchingKeys()

    if (!arraysEqual(thisKeys, thatKeys)) {
      return false
    }
    for (let i in thisKeys) {
      let k = thisKeys[i]
      if (!_.isEqual(this[k], e[k])) {
        return false
      }
    }

    return true
  }

  isSecurityAlarm(alarm) {
    const securityAlarmTypes = [
      "ALARM_SPOOFING_DEVICE",
      "ALARM_BRO_NOTICE",
      "ALARM_INTEL",
      "ALARM_VULNERABILITY"
    ];

    return securityAlarmTypes.includes(alarm.type);
  }

  jsonComparisonMatch(val, val2) {
    if (!isFinite(val2)) return false;
    let comparison = JSON.parse(val);
    if (isFinite(comparison["$gt"])) {
      if (val2 > comparison["$gt"]) {
        return true;
      }
    } else if (isFinite(comparison["$lt"])) {
      if (val2 < comparison["$lt"]) {
        return true;
      }
    } else if (isFinite(comparison["$gte"])) {
      if (val2 > comparison["$gte"] || val2 == comparison["$gte"]) {
        return true;
      }
    } else if (isFinite(comparison["$lte"])) {
      if (val2 < comparison["$lte"] || val2 == comparison["$lte"]) {
        return true;
      }
    }

    return false;
  }

  valueMatch(val, val2) {
    //special exception
    if (val.endsWith("*")) {
      if (minimatch(val2, val)) {
        return true
      }
    }

    if (val.startsWith("*.")) {
      // use glob matching
      if (!minimatch(val2, val) && // NOT glob match
        val.slice(2) !== val2) { // NOT exact sub domain match
        return false
      }
    } else {
      let cidrParts = val.split("/", 2);
      if (cidrParts.length == 2) {
        let addr = cidrParts[0];
        let mask = cidrParts[1];
        if (ip.isV4Format(addr) && RegExp("^\\d+$").test(mask) && ip.isV4Format(val2)) {
          // try matching cidr subnet iff value in alarm is an ipv4 address and value in exception is a cidr notation
          if (!ip.cidrSubnet(val).contains(val2)) {
            return false;
          }
        }
      } else {
        // not a cidr subnet exception

        // alarm might has field in number
        // and assume exceptions are always loaded from redis before comparing
        if (_.isNumber(val2)) {
          val = _.toNumber(val)
          if (isNaN(val)) return false;
        }
        if (val2 !== val) return false;
      }
    }

    return true;
  }

  getCategory() {
    return this["p.category.id"];
  }

  setCategoryMatcher(matcher) {
    this.categoryMatcher = matcher;
  }

  isExpired() {
    if (this.expireTs)
      return Date.now() / 1000 > this.expireTs;
    else
      return false;
  }

  isIdle() {
    if (this.idleTs)
      return Date.now() / 1000 < this.idleTs;
    else
      return false;
  }

  match(alarm) {
    try {
      let matched = false;

      for (const key in this) {

        if (!key.startsWith("p.") && key !== "type" && !key.startsWith("e.")) {
          continue;
        }

        if (key === "p.category.id") {
          continue;
        }

        var val = this[key];
        if (!alarm[key]) return false;
        let val2 = alarm[key];

        if (key === "type" && val === "ALARM_INTEL" && this.isSecurityAlarm(alarm)) {
          matched = true;
          continue;
        }

        if ((this["json." + key] == true || this["json." + key] == "true") && val && validator.isJSON(val)) {
          if (this.jsonComparisonMatch(val, val2)) {
            matched = true;
            continue;
          }
        }
        let val2Array = val2;
        // Exception will be parsed at object creation
        // while alarm will always use string to avoid compatibility issue with clients
        isJsonString(val2) && (val2Array = JSON.parse(val2));

        if (key.startsWith("p.tag.ids")) {
          if (_.intersection(val, val2Array.map(String)).length > 0) {
            matched = true;
            continue;
          }
        }

        if (_.isArray(val)) {
          let matchInArray = false;
          for (const valCurrent of val) {
            if (this.valueMatch(valCurrent + "", val2)) {
              matchInArray = true;
              break;
            }
          }

          if (!matchInArray) {
            return false;
          }
        } else {
          if (!this.valueMatch(val, val2)) {
            return false;
          }
        }

        matched = true;
      }

      // match against category
      if (this.categoryMatcher) {
        if (this.getCategory() === alarm["p.dest.category"]) {
          // shortcut for direct category id match
          matched = true;
        } else {
          const targetDomain = alarm["p.dest.name"];
          const targetIP = alarm["p.dest.ip"];
          log.info(`Match dest domain ${targetDomain} and ip ${targetIP} against category ${this.getCategory()}`);
          if (targetDomain && this.categoryMatcher.matchDomain(targetDomain)) {
            matched = true;
          } else if (targetIP && this.categoryMatcher.matchIP(targetIP)) {
            matched = true;
          } else {
            return false;
          }
        }
      }

      return matched;
    } catch (e) {
      log.warn("Error on alarm matching", e);
      return false;
    }
  }
}
<|endoftext|>


<commit_before>var Backbone = require('backbone');
var UndoRedo = require('../../../../../../javascripts/cartodb3/components/undo-redo/undo-redo-view.js');
var EditorModel = require('../../../../../../javascripts/cartodb3/data/editor-model.js');
var StyleModel = require('../../../../../../javascripts/cartodb3/editor/style/style-definition-model.js');

describe('components/undo-redo/undo-redo-view', function () {
  beforeEach(function () {
    this.editorModel = new EditorModel({
      edition: false
    });

    this.view = new UndoRedo({
      editorModel: new EditorModel({
        edition: false
      }),
      trackModel: new StyleModel()
    });

    spyOn(this.view, '_onRedoClick');
    spyOn(this.view, '_onUndoClick');
    this.view.render();
  });

  it('should render properly', function () {
    expect(this.view.$('button').length).toBe(2);
  });

  it('should have no leaks', function () {
    expect(this.view).toHaveNoLeaks();
  });

  it('should render apply button properly', function () {
    this.view._editorModel.set({ edition: true });
    this.view.options.applyButton = true;
    this.view.render();
    expect(this.view.$('button').length).toBe(3);
  });

  it('should render clear button properly', function () {
    var view = new UndoRedo({
      applyButton: true,
      clearButton: true,
      clearModel: new Backbone.Model({
        visible: true
      }),
      editorModel: new EditorModel({
        edition: false
      }),
      trackModel: new StyleModel()
    });

    view._editorModel.set({ edition: true });
    view.render();
    expect(view.$('button').length).toBe(4);
  });

  afterEach(function () {
    this.view.clean();
  });
});
<commit_msg>add tests for undo-redo-view<commit_after>var Backbone = require('backbone');
var UndoRedo = require('../../../../../../javascripts/cartodb3/components/undo-redo/undo-redo-view.js');
var EditorModel = require('../../../../../../javascripts/cartodb3/data/editor-model.js');
var StyleModel = require('../../../../../../javascripts/cartodb3/editor/style/style-definition-model.js');

describe('components/undo-redo/undo-redo-view', function () {
  beforeEach(function () {
    this.editorModel = new EditorModel({
      edition: false
    });

    this.view = new UndoRedo({
      editorModel: new EditorModel({
        edition: false
      }),
      trackModel: new StyleModel()
    });

    spyOn(this.view, '_onRedoClick');
    spyOn(this.view, '_onUndoClick');
    this.view.render();
  });

  it('should render properly', function () {
    expect(this.view.$('button').length).toBe(2);
  });

  it('should have no leaks', function () {
    expect(this.view).toHaveNoLeaks();
  });

  it('should render apply button properly', function () {
    this.view._editorModel.set({ edition: true });
    this.view.options.applyButton = true;
    this.view.render();
    expect(this.view.$('button').length).toBe(3);
  });

  it('should render clear button properly', function () {
    var view = new UndoRedo({
      applyButton: true,
      clearButton: true,
      clearModel: new Backbone.Model({
        visible: true
      }),
      editorModel: new EditorModel({
        edition: false
      }),
      trackModel: new StyleModel()
    });

    view._editorModel.set({ edition: true });
    view.render();
    expect(view.$('button').length).toBe(4);
  });

  it('should show the apply button as not loading by default', function () {
    expect(this.view.$el.find('.js-apply.is-loading').length).toBe(0);
    expect(this.view.$el.find('.js-apply.is-disabled').length).toBe(0);
  });

  it('should show the apply button as loading if applyStatusModel changes', function () {
    var view = new UndoRedo({
      applyButton: true,
      applyStatusModel: new Backbone.Model({
        loading: false
      }),
      editorModel: new EditorModel({
        edition: true
      }),
      trackModel: new StyleModel()
    });
    view.render();

    expect(view.$el.find('.js-apply.is-loading').length).toBe(0);
    expect(view.$el.find('.js-apply.is-disabled').length).toBe(0);

    view._applyStatusModel.set('loading', true);

    expect(view.$el.find('.js-apply.is-loading').length).toBe(1);
    expect(view.$el.find('.js-apply.is-disabled').length).toBe(1);
  });

  afterEach(function () {
    this.view.clean();
  });
});
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var parseJSDoc = require( 'doctrine' ).parse;
var remark = require( 'remark' );
var remarkLint = require( 'remark-lint' );
var remarkPlugin = require( 'remark-lint-blockquote-indentation' );
var isObject = require( '@stdlib/assert/is-object' );
var findJSDoc = require( '@stdlib/_tools/eslint/utils/find-jsdoc' );

// VARIABLES //

var DEFAULT = 2; // default indentation is 2 characters (including the `>` symbol)
var DOPTS = {
	'sloppy': true,
	'unwrap': true
};

// MAIN //

/**
* Rule for enforcing Markdown blockquote indentation in JSDoc descriptions.
*
* @param {Object} context - ESLint context
* @returns {Object} validators
*/
function main( context ) {
	var setting;
	var source;
	var config;
	var lint;

	setting = context.options[ 0 ] || DEFAULT;
	config = {
		'plugins': [
			remarkLint,
			[ remarkPlugin, [ 'error', setting ] ]
		]
	};
	lint = remark().use( config ).processSync;
	source = context.getSourceCode();

	return {
		'FunctionExpression:exit': validate,
		'FunctionDeclaration:exit': validate,
		'VariableDeclaration:exit': validate,
		'ExpressionStatement:exit': validate
	};

	/**
	* Lints JSDoc descriptions.
	*
	* @private
	* @param {ASTNode} node - AST node
	*/
	function validate( node ) {
		var jsdoc;
		var vfile;
		var ast;

		jsdoc = findJSDoc( source, node );
		if ( isObject( jsdoc ) ) {
			ast = parseJSDoc( jsdoc.value, DOPTS );
			if ( ast.description ) {
				vfile = lint( ast.description );
				if ( vfile.messages.length ) {
					reportErrors( vfile.messages, jsdoc.loc );
				}
			}
		}
	} // end FUNCTION validate()

	/**
	* Reports Markdown lint errors.
	*
	* @private
	* @param {ObjectArray} errors - Markdown lint errors
	* @param {Object} location - JSDoc location information
	*/
	function reportErrors( errors, location ) {
		var err;
		var msg;
		var loc;
		var i;

		for ( i = 0; i < errors.length; i++ ) {
			err = errors[ i ];
			msg = err.message;
			loc = copyLocationInfo( location );
			loc.start.line = err.location.start.line + 1;
			loc.start.column = err.location.start.column + 2;
			report( msg, loc );
		}
	} // end FUNCTION reportErrors()

	/**
	* Copies AST node location info.
	*
	* @private
	* @param {Object} loc - AST node location
	* @returns {Object} copied location info
	*/
	function copyLocationInfo( loc ) {
		return {
			'start': {
				'line': loc.start.line,
				'column': loc.start.column
			},
			'end': {
				'line': loc.end.line,
				'column': loc.end.column
			}
		};
	} // end FUNCTION copyLocationInfo()

	/**
	* Reports an error message.
	*
	* @private
	* @param {string} msg - error message
	* @param {Object} loc - error location info
	*/
	function report( msg, loc ) {
		context.report({
			'node': null,
			'message': msg,
			'loc': loc
		});
	} // end FUNCTION report()
} // end FUNCTION main()

// EXPORTS //

module.exports = {
	'meta': {
		'docs': {
			'description': 'enforce Markdown blockquote indentation in JSDoc descriptions'
		},
		'schema': [
			{
				'type': [
					'number',
					'string'
				]
			}
		]
	},
	'create': main
};
<commit_msg>Add line comments<commit_after>'use strict';

// MODULES //

var parseJSDoc = require( 'doctrine' ).parse;
var remark = require( 'remark' );
var remarkLint = require( 'remark-lint' );
var remarkPlugin = require( 'remark-lint-blockquote-indentation' );
var isObject = require( '@stdlib/assert/is-object' );
var findJSDoc = require( '@stdlib/_tools/eslint/utils/find-jsdoc' );

// VARIABLES //

var DEFAULT = 2; // default indentation is 2 characters (including the `>` symbol)
var DOPTS = {
	'sloppy': true,
	'unwrap': true
};

// MAIN //

/**
* Rule for enforcing Markdown blockquote indentation in JSDoc descriptions.
*
* @param {Object} context - ESLint context
* @returns {Object} validators
*/
function main( context ) {
	var setting;
	var source;
	var config;
	var lint;

	setting = context.options[ 0 ] || DEFAULT;
	config = {
		'plugins': [
			remarkLint,
			[ remarkPlugin, [ 'error', setting ] ]
		]
	};
	lint = remark().use( config ).processSync;
	source = context.getSourceCode();

	return {
		'FunctionExpression:exit': validate,
		'FunctionDeclaration:exit': validate,
		'VariableDeclaration:exit': validate,
		'ExpressionStatement:exit': validate
	};

	/**
	* Lints JSDoc descriptions.
	*
	* @private
	* @param {ASTNode} node - AST node
	*/
	function validate( node ) {
		var jsdoc;
		var vfile;
		var ast;

		jsdoc = findJSDoc( source, node );
		if ( isObject( jsdoc ) ) {
			ast = parseJSDoc( jsdoc.value, DOPTS );
			if ( ast.description ) {
				vfile = lint( ast.description );
				if ( vfile.messages.length ) {
					reportErrors( vfile.messages, jsdoc.loc );
				}
			}
		}
	} // end FUNCTION validate()

	/**
	* Reports Markdown lint errors.
	*
	* @private
	* @param {ObjectArray} errors - Markdown lint errors
	* @param {Object} location - JSDoc location information
	*/
	function reportErrors( errors, location ) {
		var err;
		var msg;
		var loc;
		var i;

		for ( i = 0; i < errors.length; i++ ) {
			err = errors[ i ];
			msg = err.message;
			loc = copyLocationInfo( location );
			loc.start.line = err.location.start.line + 1; // Note: we assume `/**` is on its own line
			loc.start.column = err.location.start.column + 2; // Note: we assume that 1 space separates `*` from JSDoc description content (e.g., `* ## Beep`)
			report( msg, loc );
		}
	} // end FUNCTION reportErrors()

	/**
	* Copies AST node location info.
	*
	* @private
	* @param {Object} loc - AST node location
	* @returns {Object} copied location info
	*/
	function copyLocationInfo( loc ) {
		return {
			'start': {
				'line': loc.start.line,
				'column': loc.start.column
			},
			'end': {
				'line': loc.end.line,
				'column': loc.end.column
			}
		};
	} // end FUNCTION copyLocationInfo()

	/**
	* Reports an error message.
	*
	* @private
	* @param {string} msg - error message
	* @param {Object} loc - error location info
	*/
	function report( msg, loc ) {
		context.report({
			'node': null,
			'message': msg,
			'loc': loc
		});
	} // end FUNCTION report()
} // end FUNCTION main()

// EXPORTS //

module.exports = {
	'meta': {
		'docs': {
			'description': 'enforce Markdown blockquote indentation in JSDoc descriptions'
		},
		'schema': [
			{
				'type': [
					'number',
					'string'
				]
			}
		]
	},
	'create': main
};
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var atomify = require('atomify');

module.exports = function(grunt) {

  grunt.task.registerMultiTask('atomify', 'Atomify all the stuff grunt says to.', function() {

        var options = this.data;
        var done = this.async();

        var cssConfig = options.cssConfig;
        var jsConfig = options.jsConfig;
        var atomifyConfig = {};
        var expectedCallbacks = 0;
        var receivedCallbacks = 0;

        function mkdirIfNeeded(outputFile)  {
            var lastSlash, path,
                directory = '', directories;

            if(outputFile.lastIndexOf('/') > 0)  {
                // mkdir the output if it doesn't exist
                lastSlash = outputFile.lastIndexOf('/');
                path = outputFile.slice(0, lastSlash);

                if(fs.existsSync(path) === false)  {
                    directories = path.split('/').forEach(function(element, index, array)  {
                        directory += element + '/';
                        fs.mkdirSync(directory);
                    });
                }
            }

        }

        if(jsConfig !== undefined)  {
            // if there's a directory in the output, we have to make it
            mkdirIfNeeded(jsConfig.output);
            // add the JS config to the overall config that we're passing to atomify
            atomifyConfig.js = jsConfig;

            if(jsConfig.output !== undefined)  {
                expectedCallbacks +=1;
            }
        }

        if(cssConfig !== undefined)  {
            // if there's a directory in the output, we have to make it
            mkdirIfNeeded(cssConfig.output);
            // add the CSS config to the overall config that we're passing to atomify
            atomifyConfig.css = cssConfig;

            if(cssConfig.output !== undefined)  {
                expectedCallbacks +=1;
            }
        }

        if((cssConfig !== undefined) || (jsConfig !== undefined))  {
            atomify(atomifyConfig, function(error)  {
                receivedCallbacks +=1;
                if(receivedCallbacks === expectedCallbacks)  {
                    done();
                }
            });
        }  else   {
            done();  // nothing to do. done.
        }

  });

};
<commit_msg>jsConfig and cssConfig should be top level properties named js and css<commit_after>'use strict';

var fs = require('fs');
var atomify = require('atomify');

module.exports = function(grunt) {

  grunt.task.registerMultiTask('atomify', 'Atomify all the stuff grunt says to.', function() {

        var options = this.data;
        var done = this.async();

        var cssConfig = options.css;
        var jsConfig = options.js;
        var atomifyConfig = {};
        var expectedCallbacks = 0;
        var receivedCallbacks = 0;

        function mkdirIfNeeded(outputFile)  {
            var lastSlash, path,
                directory = '', directories;

            if(outputFile.lastIndexOf('/') > 0)  {
                // mkdir the output if it doesn't exist
                lastSlash = outputFile.lastIndexOf('/');
                path = outputFile.slice(0, lastSlash);

                if(fs.existsSync(path) === false)  {
                    directories = path.split('/').forEach(function(element, index, array)  {
                        directory += element + '/';
                        fs.mkdirSync(directory);
                    });
                }
            }

        }

        if(jsConfig !== undefined)  {
            // if there's a directory in the output, we have to make it
            mkdirIfNeeded(jsConfig.output);
            // add the JS config to the overall config that we're passing to atomify
            atomifyConfig.js = jsConfig;

            if(jsConfig.output !== undefined)  {
                expectedCallbacks +=1;
            }
        }

        if(cssConfig !== undefined)  {
            // if there's a directory in the output, we have to make it
            mkdirIfNeeded(cssConfig.output);
            // add the CSS config to the overall config that we're passing to atomify
            atomifyConfig.css = cssConfig;

            if(cssConfig.output !== undefined)  {
                expectedCallbacks +=1;
            }
        }

        if((cssConfig !== undefined) || (jsConfig !== undefined))  {
            atomify(atomifyConfig, function(error)  {
                receivedCallbacks +=1;
                if(receivedCallbacks === expectedCallbacks)  {
                    done();
                }
            });
        }  else   {
            done();  // nothing to do. done.
        }

  });

};
<|endoftext|>


<commit_before>/*
 * grunt-gitinfo
 * https://github.com/damkraw/grunt-gitinfo
 *
 * Copyright (c) 2013 Damian Krawczyk, Corey Jewett, Maciej Lisiewski, Tom Gault, kolya-ay, nerdgore, madarche, drasive
 * Licensed under the MIT license.
 */

// JSLint
/*global module, require, console*/
/*jslint nomen: true */

module.exports = function (grunt) {
    'use strict';

    var _ = require('lodash'),
        async = require('async'),
        getobject = require('getobject');

    grunt.registerTask('gitinfo', 'Gather information about the git repository.', function () {
        var done = this.async(),
            gitinfo = {},

            // Retrieve our config object, filling in missing items with defaults.
            config = _.merge({
                options: {
                    cwd: null
                },
                commands : {
                    'local.branch.current.name'              : ['rev-parse', '--abbrev-ref', 'HEAD'],
                    'local.branch.current.SHA'               : ['rev-parse', 'HEAD'],
                    'local.branch.current.shortSHA'          : ['rev-parse', '--short', 'HEAD'],
                    'local.branch.current.currentUser'       : ['config', '--global', 'user.name'],
                    'local.branch.current.lastCommitTime'    : ['log', '--format="%ai"', '-n1', 'HEAD'],
                    'local.branch.current.lastCommitMessage' : ['log', '--format="%B"', '-n1', 'HEAD'],
                    'local.branch.current.lastCommitAuthor'  : ['log', '--format="%aN"', '-n1', 'HEAD'],
                    'local.branch.current.lastCommitNumber'  : ['rev-list', '--count', 'HEAD'],
                    'remote.origin.url'                      : ['config', '--get-all', 'remote.origin.url']
                }
            }, grunt.config.get('gitinfo')),

            work = function (conf_key, cb) {
                var spawn_args = config.commands[conf_key];

                grunt.util.spawn(
                    {
                        cmd  : 'git',
                        args : spawn_args,
                        opts : {
                            cwd : config.options.cwd
                        }
                    },
                    function (err, result) {
                        if (err) {
                            grunt.log.debug("Couldn't set config:", conf_key);
                        } else {
                            getobject.set(gitinfo, conf_key, result.stdout);

                            if (grunt.option("verbose")) {
                                // could be unnecessary
                                grunt.verbose.ok(conf_key, "=", result.stdout);
                            } else {
                                grunt.log.debug(conf_key, "=", result.stdout);
                            }
                        }
                        cb();
                    }
                );
            },

            fin = function () {
                var merged = _.defaults(gitinfo, grunt.config.get('gitinfo'));
                grunt.config.set('gitinfo', merged);
                done();
            };

        grunt.verbose.writeflags(config.options, 'config.options');

        async.each(_.keys(config.commands), work, fin);
    });
};
<commit_msg>Fix using author-date and author-name instead of commit-date and commit-name<commit_after>/*
 * grunt-gitinfo
 * https://github.com/damkraw/grunt-gitinfo
 *
 * Copyright (c) 2013 Damian Krawczyk, Corey Jewett, Maciej Lisiewski, Tom Gault, kolya-ay, nerdgore, madarche, drasive
 * Licensed under the MIT license.
 */

// JSLint
/*global module, require, console*/
/*jslint nomen: true */

module.exports = function (grunt) {
    'use strict';

    var _ = require('lodash'),
        async = require('async'),
        getobject = require('getobject');

    grunt.registerTask('gitinfo', 'Gather information about the git repository.', function () {
        var done = this.async(),
            gitinfo = {},

            // Retrieve our config object, filling in missing items with defaults.
            config = _.merge({
                options: {
                    cwd: null
                },
                commands : {
                    'local.branch.current.name'              : ['rev-parse', '--abbrev-ref', 'HEAD'],
                    'local.branch.current.SHA'               : ['rev-parse', 'HEAD'],
                    'local.branch.current.shortSHA'          : ['rev-parse', '--short', 'HEAD'],
                    'local.branch.current.currentUser'       : ['config', '--global', 'user.name'],
                    'local.branch.current.lastCommitTime'    : ['log', '--format="%ci"', '-n1', 'HEAD'],
                    'local.branch.current.lastCommitMessage' : ['log', '--format="%B"', '-n1', 'HEAD'],
                    'local.branch.current.lastCommitAuthor'  : ['log', '--format="%cN"', '-n1', 'HEAD'],
                    'local.branch.current.lastCommitNumber'  : ['rev-list', '--count', 'HEAD'],
                    'remote.origin.url'                      : ['config', '--get-all', 'remote.origin.url']
                }
            }, grunt.config.get('gitinfo')),

            work = function (conf_key, cb) {
                var spawn_args = config.commands[conf_key];

                grunt.util.spawn(
                    {
                        cmd  : 'git',
                        args : spawn_args,
                        opts : {
                            cwd : config.options.cwd
                        }
                    },
                    function (err, result) {
                        if (err) {
                            grunt.log.debug("Couldn't set config:", conf_key);
                        } else {
                            getobject.set(gitinfo, conf_key, result.stdout);

                            if (grunt.option("verbose")) {
                                // could be unnecessary
                                grunt.verbose.ok(conf_key, "=", result.stdout);
                            } else {
                                grunt.log.debug(conf_key, "=", result.stdout);
                            }
                        }
                        cb();
                    }
                );
            },

            fin = function () {
                var merged = _.defaults(gitinfo, grunt.config.get('gitinfo'));
                grunt.config.set('gitinfo', merged);
                done();
            };

        grunt.verbose.writeflags(config.options, 'config.options');

        async.each(_.keys(config.commands), work, fin);
    });
};
<|endoftext|>