<commit_before>/*
 *
 * (c) Copyright Ascensio System Limited 2010-2016
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
*/
/**
 *  Button.js
 *
 *  Created by Alexander Yuzhin on 1/20/14
 *  Copyright (c) 2014 Ascensio System SIA. All rights reserved.
 *
 */

/**
 *  Using template
 *
 *  A simple button with text:
 *  <button type="button" class="btn" id="id-button">Caption</button>
 *
 *  A simple button with icon:
 *  <button type="button" class="btn" id="id-button"> </button>
 *
 *  A button with menu:
 *

 *      <button type="button" class="btn dropdown-toggle" data-toggle="dropdown">
 *
 *
 *      </button>
 *

 *

 *

 *
 *  A split button:
 *

 *      <button type="button" class="btn"> </button>
 *      <button type="button" class="btn dropdown-toggle" data-toggle="dropdown">
 *
 *
 *      </button>
 *

 *

 *

 *
 *   A useful classes of button size
 *
 *  - `'small'`
 *  - `'normal'`
 *  - `'large'`
 *  - `'huge'`
 *
 *  A useful classes of button type
 *
 *  - `'default'`
 *  - `'active'`
 *
 *
 *  Buttons can also be toggled. To enable this, you simple set the {@link #enableToggle} property to `true`.
 *
 *  Example usage:
 *      new Common.UI.Button({
 *          el: $('#id'),
 *          enableToggle: true
 *      });
 *
 *
 *  @property {Boolean} disabled
 *  True if this button is disabled. Read-only.
 *
 *  disabled: false,
 *
 *
 *  @property {Boolean} pressed
 *  True if this button is pressed (only if enableToggle = true). Read-only.
 *
 *  pressed: false,
 *
 *
 *  @cfg {Boolean} [allowDepress=true]
 *  False to not allow a pressed Button to be depressed. Only valid when {@link #enableToggle} is true.
 *
 *  @cfg {String/Object} hint
 *  The tooltip for the button - can be a string to be used as bootstrap tooltip
 *
 */

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/BaseView',
    'common/main/lib/component/ToggleManager'
], function () {
    'use strict';

    Common.UI.Button = Common.UI.BaseView.extend({
        options : {
            id              : null,
            hint            : false,
            enableToggle    : false,
            allowDepress    : true,
            toggleGroup     : null,
            cls             : '',
            iconCls         : '',
            caption         : '',
            menu            : null,
            disabled        : false,
            pressed         : false,
            split           : false
        },

        template: _.template([
            '<% if (menu == null) { %>',
                '<button type="button" class="btn <%= cls %>" id="<%= id %>" style="<%= style %>">',
                    '<%= caption %>',
                    '<% if (iconCls != "") { %>',
                        '<span class="btn-icon <%= iconCls %>"> ',
                    '<% } %>',
                '</button>',
            '<% } else if (split == false) {%>',
                '<div class="btn-group" id="<%= id %>" style="<%= style %>">',
                    '<button type="button" class="btn dropdown-toggle <%= cls %>" data-toggle="dropdown">',
                        '<%= caption %>',
                        '<% if (iconCls != "") { %>',
                            '<span class="btn-icon <%= iconCls %>"> ',
                        '<% } %>',
                        '',
                    '</button>',
                '
',
            '<% } else { %>',
                '<div class="btn-group split" id="<%= id %>" style="<%= style %>">',
                    '<button type="button" class="btn <%= cls %>">',
                        '<%= caption %>',
                        '<% if (iconCls != "") { %>',
                            '<span class="btn-icon <%= iconCls %>"> ',
                        '<% } %>',
                    '</button>',
                    '<button type="button" class="btn <%= cls %> dropdown-toggle" data-toggle="dropdown">',
                        '',
                        '',
                    '</button>',
                '
',
            '<% } %>'
        ].join('')),

        initialize : function(options) {
            Common.UI.BaseView.prototype.initialize.call(this, options);

            var me = this;

            me.id           = me.options.id || Common.UI.getId();
            me.hint         = me.options.hint;
            me.enableToggle = me.options.enableToggle;
            me.allowDepress = me.options.allowDepress;
            me.cls          = me.options.cls;
            me.iconCls      = me.options.iconCls;
            me.menu         = me.options.menu;
            me.split        = me.options.split;
            me.toggleGroup  = me.options.toggleGroup;
            me.disabled     = me.options.disabled;
            me.pressed      = me.options.pressed;
            me.caption      = me.options.caption;
            me.template     = me.options.template || me.template;
            me.style        = me.options.style;
            me.rendered     = false;

            if (me.options.el) {
                me.render();
            }
        },

        render: function(parentEl) {
            var me = this;

            me.trigger('render:before', me);

            me.cmpEl = $(me.el);

            if (parentEl) {
                me.setElement(parentEl, false);

                if (!me.rendered) {
                    me.cmpEl = $(this.template({
                        id           : me.id,
                        cls          : me.cls,
                        iconCls      : me.iconCls,
                        menu         : me.menu,
                        split        : me.split,
                        disabled     : me.disabled,
                        pressed      : me.pressed,
                        caption      : me.caption,
                        style        : me.style
                    }));

                    if (me.menu && _.isObject(me.menu) && _.isFunction(me.menu.render))
                        me.menu.render(me.cmpEl);

                    parentEl.html(me.cmpEl);
                }
            }

            if (!me.rendered) {
                var el = me.cmpEl,
                    isGroup = el.hasClass('btn-group'),
                    isSplit = el.hasClass('split');

                if (me.options.hint) {
                    var modalParents = me.cmpEl.closest('.asc-window');

                    me.cmpEl.attr('data-toggle', 'tooltip');
                    me.cmpEl.tooltip({
                        title       : me.options.hint,
                        placement   : me.options.hintAnchor||'cursor'
                    });

                    if (modalParents.length > 0) {
                        me.cmpEl.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10);
                    }
                }

                if (_.isString(me.toggleGroup)) {
                    me.enableToggle = true;
                }

                var buttonHandler = function(e) {
                    if (!me.disabled && e.which == 1) {
                        me.doToggle();
                        if (me.options.hint) {
                            var tip = me.cmpEl.data('bs.tooltip');
                            if (tip) {
                                if (tip.dontShow===undefined)
                                    tip.dontShow = true;

                                tip.hide();
                            }
                        }
                        me.trigger('click', me, e);
                    }
                };

                var doSplitSelect = function(select, e) {
                    if (!select) {
                        // Is mouse under button
                        var isUnderMouse = false;

                        _.each($('button', el), function(el){
                            if ($(el).is(':hover')) {
                                isUnderMouse = true;
                                return false;
                            }
                        });

                        if (!isUnderMouse) {
                            el.removeClass('over');
                            $('button', el).removeClass('over');
                        }
                    }

                    if (!select && (me.enableToggle && me.allowDepress && me.pressed))
                        return;
                    if (select && !isSplit && (me.enableToggle && me.allowDepress && !me.pressed)) { // to depress button with menu
                        e.preventDefault();
                        return;
                    }

                    $('button:first', el).toggleClass('active', select);
                    $('[data-toggle^=dropdown]', el).toggleClass('active', select);
                    el.toggleClass('active', select);
                };

                var menuHandler = function(e) {
                    if (!me.disabled && e.which == 1) {
                        if (isSplit) {
                            if (me.options.hint) {
                                var tip = me.cmpEl.data('bs.tooltip');
                                if (tip) {
                                    if (tip.dontShow===undefined)
                                        tip.dontShow = true;

                                    tip.hide();
                                }
                            }
                            var isOpen = el.hasClass('open');
                            doSplitSelect(!isOpen, e);
                        }
                    }
                };

                var doSetActiveState = function(e, state) {
                    if (isSplit) {
                        doSplitSelect(state, e);
                    } else {
                        el.toggleClass('active', state);
                        $('button', el).toggleClass('active', state);
                    }
                };

                var onMouseDown = function (e) {
                    doSplitSelect(true, e);
                    $(document).on('mouseup',   onMouseUp);
                };

                var onMouseUp = function (e) {
                    doSplitSelect(false, e);
                    $(document).off('mouseup',   onMouseUp);
                };

                var onAfterHideMenu = function(e) {
                    me.cmpEl.find('.dropdown-toggle').blur();
                    if (me.cmpEl.hasClass('active') !== me.pressed)
                        me.cmpEl.trigger('button.internal.active', [me.pressed]);
                };

                if (isGroup) {
                    if (isSplit) {
                        $('[data-toggle^=dropdown]', el).on('mousedown', _.bind(menuHandler, this));
                        $('button', el).on('mousedown', _.bind(onMouseDown, this));
                    }

                    el.on('hide.bs.dropdown', _.bind(doSplitSelect, me, false));
                    el.on('show.bs.dropdown', _.bind(doSplitSelect, me, true));
                    el.on('hidden.bs.dropdown', _.bind(onAfterHideMenu, me));

                    $('button:first', el).on('click', buttonHandler);
                } else {
                    el.on('click', buttonHandler);
                }

                el.on('button.internal.active', _.bind(doSetActiveState, me));

                el.on('mouseover', function(e) {
                    if (!me.disabled) {
                        me.cmpEl.addClass('over');
                        me.trigger('mouseover', me, e);
                    }
                });

                el.on('mouseout', function(e) {
                    if (!me.disabled) {
                        me.cmpEl.removeClass('over');
                        me.trigger('mouseout', me, e);
                    }
                });

                // Register the button in the toggle manager
                Common.UI.ToggleManager.register(me);
            }

            me.rendered = true;

            if (me.pressed) {
                me.toggle(me.pressed, true);
            }

            if (me.disabled) {
                me.setDisabled(!(me.disabled=false));
            }

            me.trigger('render:after', me);

            return this;
        },

        doToggle: function(){
            var me = this;
            if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
                me.toggle();
            }
        },

        toggle: function(toggle, suppressEvent) {
            var state = toggle === undefined ? !this.pressed : !!toggle;

            this.pressed = state;

            if (this.cmpEl)
                this.cmpEl.trigger('button.internal.active', [state]);

            if (!suppressEvent)
                this.trigger('toggle', this, state);
        },

        isActive: function() {
            if (this.enableToggle)
                return this.pressed;

            return this.cmpEl.hasClass('active')
        },

        setDisabled: function(disabled) {
            if (this.rendered && this.disabled != disabled) {
                var el = this.cmpEl,
                    isGroup = el.hasClass('btn-group');

                disabled = (disabled===true);

                if (disabled !== el.hasClass('disabled')) {
                    var decorateBtn = function(button) {
                        button.toggleClass('disabled', disabled);
                        (disabled) ? button.attr({disabled: disabled}) : button.removeAttr('disabled');
                    };

                    decorateBtn(el);
                    isGroup && decorateBtn(el.children('button'));
                }

                if (disabled) {
                    var tip = this.cmpEl.data('bs.tooltip');
                    if (tip) {
                        tip.hide();
                    }
                }
            }

            this.disabled = disabled;
        },

        isDisabled: function() {
            return this.disabled;
        },

        setIconCls: function(cls) {
            var btnIconEl = $(this.el).find('span.btn-icon'),
                oldCls = this.iconCls;

            this.iconCls = cls;
            btnIconEl.removeClass(oldCls);
            btnIconEl.addClass(cls || '');
        },

        setVisible: function(visible) {
            if (this.cmpEl) this.cmpEl.toggleClass('hidden', !visible);
        },

        updateHint: function(hint) {
            this.options.hint = hint;

            if (!this.rendered) return;

            var cmpEl = this.cmpEl,
                modalParents = cmpEl.closest('.asc-window');

            cmpEl.attr('data-toggle', 'tooltip');
            cmpEl.tooltip('destroy').tooltip({
                title       : hint,
                placement   : this.options.hintAnchor || 'cursor'
            });

            if (modalParents.length > 0) {
                cmpEl.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10);
            }
        },

        setCaption: function(caption) {
            if (this.caption != caption) {
                this.caption = caption;

                if (this.rendered) {
                    var captionNode = this.cmpEl.find('button:first > .caption').andSelf().filter('button > .caption');

                    if (captionNode.length > 0) {
                        captionNode.text(caption);
                    } else {
                        this.cmpEl.find('button:first').andSelf().filter('button').text(caption);
                    }
                }
            }
        },

        setMenu: function (m) {
            if (this.rendered && m && _.isObject(m) && _.isFunction(m.render)){
                this.menu = m;
                this.menu.render(this.cmpEl);
            }
        }
    });
});

<commit_msg>Button: set menu when button isn't rendered.<commit_after>/*
 *
 * (c) Copyright Ascensio System Limited 2010-2016
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
*/
/**
 *  Button.js
 *
 *  Created by Alexander Yuzhin on 1/20/14
 *  Copyright (c) 2014 Ascensio System SIA. All rights reserved.
 *
 */

/**
 *  Using template
 *
 *  A simple button with text:
 *  <button type="button" class="btn" id="id-button">Caption</button>
 *
 *  A simple button with icon:
 *  <button type="button" class="btn" id="id-button"> </button>
 *
 *  A button with menu:
 *

 *      <button type="button" class="btn dropdown-toggle" data-toggle="dropdown">
 *
 *
 *      </button>
 *

 *

 *

 *
 *  A split button:
 *

 *      <button type="button" class="btn"> </button>
 *      <button type="button" class="btn dropdown-toggle" data-toggle="dropdown">
 *
 *
 *      </button>
 *

 *

 *

 *
 *   A useful classes of button size
 *
 *  - `'small'`
 *  - `'normal'`
 *  - `'large'`
 *  - `'huge'`
 *
 *  A useful classes of button type
 *
 *  - `'default'`
 *  - `'active'`
 *
 *
 *  Buttons can also be toggled. To enable this, you simple set the {@link #enableToggle} property to `true`.
 *
 *  Example usage:
 *      new Common.UI.Button({
 *          el: $('#id'),
 *          enableToggle: true
 *      });
 *
 *
 *  @property {Boolean} disabled
 *  True if this button is disabled. Read-only.
 *
 *  disabled: false,
 *
 *
 *  @property {Boolean} pressed
 *  True if this button is pressed (only if enableToggle = true). Read-only.
 *
 *  pressed: false,
 *
 *
 *  @cfg {Boolean} [allowDepress=true]
 *  False to not allow a pressed Button to be depressed. Only valid when {@link #enableToggle} is true.
 *
 *  @cfg {String/Object} hint
 *  The tooltip for the button - can be a string to be used as bootstrap tooltip
 *
 */

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/BaseView',
    'common/main/lib/component/ToggleManager'
], function () {
    'use strict';

    Common.UI.Button = Common.UI.BaseView.extend({
        options : {
            id              : null,
            hint            : false,
            enableToggle    : false,
            allowDepress    : true,
            toggleGroup     : null,
            cls             : '',
            iconCls         : '',
            caption         : '',
            menu            : null,
            disabled        : false,
            pressed         : false,
            split           : false
        },

        template: _.template([
            '<% if (menu == null) { %>',
                '<button type="button" class="btn <%= cls %>" id="<%= id %>" style="<%= style %>">',
                    '<%= caption %>',
                    '<% if (iconCls != "") { %>',
                        '<span class="btn-icon <%= iconCls %>"> ',
                    '<% } %>',
                '</button>',
            '<% } else if (split == false) {%>',
                '<div class="btn-group" id="<%= id %>" style="<%= style %>">',
                    '<button type="button" class="btn dropdown-toggle <%= cls %>" data-toggle="dropdown">',
                        '<%= caption %>',
                        '<% if (iconCls != "") { %>',
                            '<span class="btn-icon <%= iconCls %>"> ',
                        '<% } %>',
                        '',
                    '</button>',
                '
',
            '<% } else { %>',
                '<div class="btn-group split" id="<%= id %>" style="<%= style %>">',
                    '<button type="button" class="btn <%= cls %>">',
                        '<%= caption %>',
                        '<% if (iconCls != "") { %>',
                            '<span class="btn-icon <%= iconCls %>"> ',
                        '<% } %>',
                    '</button>',
                    '<button type="button" class="btn <%= cls %> dropdown-toggle" data-toggle="dropdown">',
                        '',
                        '',
                    '</button>',
                '
',
            '<% } %>'
        ].join('')),

        initialize : function(options) {
            Common.UI.BaseView.prototype.initialize.call(this, options);

            var me = this;

            me.id           = me.options.id || Common.UI.getId();
            me.hint         = me.options.hint;
            me.enableToggle = me.options.enableToggle;
            me.allowDepress = me.options.allowDepress;
            me.cls          = me.options.cls;
            me.iconCls      = me.options.iconCls;
            me.menu         = me.options.menu;
            me.split        = me.options.split;
            me.toggleGroup  = me.options.toggleGroup;
            me.disabled     = me.options.disabled;
            me.pressed      = me.options.pressed;
            me.caption      = me.options.caption;
            me.template     = me.options.template || me.template;
            me.style        = me.options.style;
            me.rendered     = false;

            if (me.options.el) {
                me.render();
            }
        },

        render: function(parentEl) {
            var me = this;

            me.trigger('render:before', me);

            me.cmpEl = $(me.el);

            if (parentEl) {
                me.setElement(parentEl, false);

                if (!me.rendered) {
                    me.cmpEl = $(this.template({
                        id           : me.id,
                        cls          : me.cls,
                        iconCls      : me.iconCls,
                        menu         : me.menu,
                        split        : me.split,
                        disabled     : me.disabled,
                        pressed      : me.pressed,
                        caption      : me.caption,
                        style        : me.style
                    }));

                    if (me.menu && _.isObject(me.menu) && _.isFunction(me.menu.render))
                        me.menu.render(me.cmpEl);

                    parentEl.html(me.cmpEl);
                }
            }

            if (!me.rendered) {
                var el = me.cmpEl,
                    isGroup = el.hasClass('btn-group'),
                    isSplit = el.hasClass('split');

                if (me.options.hint) {
                    var modalParents = me.cmpEl.closest('.asc-window');

                    me.cmpEl.attr('data-toggle', 'tooltip');
                    me.cmpEl.tooltip({
                        title       : me.options.hint,
                        placement   : me.options.hintAnchor||'cursor'
                    });

                    if (modalParents.length > 0) {
                        me.cmpEl.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10);
                    }
                }

                if (_.isString(me.toggleGroup)) {
                    me.enableToggle = true;
                }

                var buttonHandler = function(e) {
                    if (!me.disabled && e.which == 1) {
                        me.doToggle();
                        if (me.options.hint) {
                            var tip = me.cmpEl.data('bs.tooltip');
                            if (tip) {
                                if (tip.dontShow===undefined)
                                    tip.dontShow = true;

                                tip.hide();
                            }
                        }
                        me.trigger('click', me, e);
                    }
                };

                var doSplitSelect = function(select, e) {
                    if (!select) {
                        // Is mouse under button
                        var isUnderMouse = false;

                        _.each($('button', el), function(el){
                            if ($(el).is(':hover')) {
                                isUnderMouse = true;
                                return false;
                            }
                        });

                        if (!isUnderMouse) {
                            el.removeClass('over');
                            $('button', el).removeClass('over');
                        }
                    }

                    if (!select && (me.enableToggle && me.allowDepress && me.pressed))
                        return;
                    if (select && !isSplit && (me.enableToggle && me.allowDepress && !me.pressed)) { // to depress button with menu
                        e.preventDefault();
                        return;
                    }

                    $('button:first', el).toggleClass('active', select);
                    $('[data-toggle^=dropdown]', el).toggleClass('active', select);
                    el.toggleClass('active', select);
                };

                var menuHandler = function(e) {
                    if (!me.disabled && e.which == 1) {
                        if (isSplit) {
                            if (me.options.hint) {
                                var tip = me.cmpEl.data('bs.tooltip');
                                if (tip) {
                                    if (tip.dontShow===undefined)
                                        tip.dontShow = true;

                                    tip.hide();
                                }
                            }
                            var isOpen = el.hasClass('open');
                            doSplitSelect(!isOpen, e);
                        }
                    }
                };

                var doSetActiveState = function(e, state) {
                    if (isSplit) {
                        doSplitSelect(state, e);
                    } else {
                        el.toggleClass('active', state);
                        $('button', el).toggleClass('active', state);
                    }
                };

                var onMouseDown = function (e) {
                    doSplitSelect(true, e);
                    $(document).on('mouseup',   onMouseUp);
                };

                var onMouseUp = function (e) {
                    doSplitSelect(false, e);
                    $(document).off('mouseup',   onMouseUp);
                };

                var onAfterHideMenu = function(e) {
                    me.cmpEl.find('.dropdown-toggle').blur();
                    if (me.cmpEl.hasClass('active') !== me.pressed)
                        me.cmpEl.trigger('button.internal.active', [me.pressed]);
                };

                if (isGroup) {
                    if (isSplit) {
                        $('[data-toggle^=dropdown]', el).on('mousedown', _.bind(menuHandler, this));
                        $('button', el).on('mousedown', _.bind(onMouseDown, this));
                    }

                    el.on('hide.bs.dropdown', _.bind(doSplitSelect, me, false));
                    el.on('show.bs.dropdown', _.bind(doSplitSelect, me, true));
                    el.on('hidden.bs.dropdown', _.bind(onAfterHideMenu, me));

                    $('button:first', el).on('click', buttonHandler);
                } else {
                    el.on('click', buttonHandler);
                }

                el.on('button.internal.active', _.bind(doSetActiveState, me));

                el.on('mouseover', function(e) {
                    if (!me.disabled) {
                        me.cmpEl.addClass('over');
                        me.trigger('mouseover', me, e);
                    }
                });

                el.on('mouseout', function(e) {
                    if (!me.disabled) {
                        me.cmpEl.removeClass('over');
                        me.trigger('mouseout', me, e);
                    }
                });

                // Register the button in the toggle manager
                Common.UI.ToggleManager.register(me);
            }

            me.rendered = true;

            if (me.pressed) {
                me.toggle(me.pressed, true);
            }

            if (me.disabled) {
                me.setDisabled(!(me.disabled=false));
            }

            me.trigger('render:after', me);

            return this;
        },

        doToggle: function(){
            var me = this;
            if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
                me.toggle();
            }
        },

        toggle: function(toggle, suppressEvent) {
            var state = toggle === undefined ? !this.pressed : !!toggle;

            this.pressed = state;

            if (this.cmpEl)
                this.cmpEl.trigger('button.internal.active', [state]);

            if (!suppressEvent)
                this.trigger('toggle', this, state);
        },

        isActive: function() {
            if (this.enableToggle)
                return this.pressed;

            return this.cmpEl.hasClass('active')
        },

        setDisabled: function(disabled) {
            if (this.rendered && this.disabled != disabled) {
                var el = this.cmpEl,
                    isGroup = el.hasClass('btn-group');

                disabled = (disabled===true);

                if (disabled !== el.hasClass('disabled')) {
                    var decorateBtn = function(button) {
                        button.toggleClass('disabled', disabled);
                        (disabled) ? button.attr({disabled: disabled}) : button.removeAttr('disabled');
                    };

                    decorateBtn(el);
                    isGroup && decorateBtn(el.children('button'));
                }

                if (disabled) {
                    var tip = this.cmpEl.data('bs.tooltip');
                    if (tip) {
                        tip.hide();
                    }
                }
            }

            this.disabled = disabled;
        },

        isDisabled: function() {
            return this.disabled;
        },

        setIconCls: function(cls) {
            var btnIconEl = $(this.el).find('span.btn-icon'),
                oldCls = this.iconCls;

            this.iconCls = cls;
            btnIconEl.removeClass(oldCls);
            btnIconEl.addClass(cls || '');
        },

        setVisible: function(visible) {
            if (this.cmpEl) this.cmpEl.toggleClass('hidden', !visible);
        },

        updateHint: function(hint) {
            this.options.hint = hint;

            if (!this.rendered) return;

            var cmpEl = this.cmpEl,
                modalParents = cmpEl.closest('.asc-window');

            cmpEl.attr('data-toggle', 'tooltip');
            cmpEl.tooltip('destroy').tooltip({
                title       : hint,
                placement   : this.options.hintAnchor || 'cursor'
            });

            if (modalParents.length > 0) {
                cmpEl.data('bs.tooltip').tip().css('z-index', parseInt(modalParents.css('z-index')) + 10);
            }
        },

        setCaption: function(caption) {
            if (this.caption != caption) {
                this.caption = caption;

                if (this.rendered) {
                    var captionNode = this.cmpEl.find('button:first > .caption').andSelf().filter('button > .caption');

                    if (captionNode.length > 0) {
                        captionNode.text(caption);
                    } else {
                        this.cmpEl.find('button:first').andSelf().filter('button').text(caption);
                    }
                }
            }
        },

        setMenu: function (m) {
            if (m && _.isObject(m) && _.isFunction(m.render)){
                this.menu = m;
                if (this.rendered)
                    this.menu.render(this.cmpEl);
            }
        }
    });
});

<|endoftext|>


<commit_before>var appConfig = require('./passport-config'),
    passport = require('passport'),
    passportOauth = require('passport-oauth'),
    OAuthStrategy = passportOauth.OAuthStrategy,
    OAuth2Strategy = passportOauth.OAuth2Strategy,
    request = require('request');

var passportPlugin = {};

var logins = [];
var users = [];

var hasApi = function(apiName) {
  return appConfig[apiName] != undefined;
};
passportPlugin.hasApi = hasApi;

var findUser = function(apiName,req) {

console.log(JSON.stringify(logins));

  if(req.session.user == undefined
     || logins[req.session.user.id] == undefined) {
    return undefined;
  }

  return users[logins[req.session.user.id][apiName]];
};
passportPlugin.findUser = findUser;

var init = function(app) {

  app.use(passport.initialize());
  app.use(passport.session());

  // store user in persistent storage for passport
  passport.serializeUser(function(user, done) {
    //console.log('serialize user ' + user._id);
    users[user._id] = user;
    done(null, user._id);
  });

  // retrieve user from persistent store for passport
  passport.deserializeUser(function(id, done) {
    //console.log('deserialize user ' + id);
    var user = users[id];
    done(null, user);
  });

  // load all app configs
  for(var apiName in appConfig) {
    console.log('register = ' + apiName);
    register(apiName);
  }

};
passportPlugin.init = init;

var register = function(apiName) {

   var strategyCallback = function(req, token, tokenSecret, profile, done) {
    var fakeUser = { 'token' : token,
                     'tokenSecret' : tokenSecret };
    var options =  { 'method' : 'GET',
                     'uri' : appConfig[apiName].defaultLoadUrl };
    var callback = function(user, data) {
      var fullUser = null;

      //var profileUser = appConfig[apiName].userElementName?data[appConfig[apiName].userElementName]:data;
      var profileUserId = null;
      if(data) {
        var userIdPath = appConfig[apiName].userIdPath.split('.');
        var profileUser = data;
        for(var i=0;i<userIdPath.length;i++) {
          if(i==userIdPath.length-1) {
            profileUserId = profileUser[userIdPath[i]];
          }
          else {
            profileUser = profileUser[userIdPath[i]];
          }
        }
      }

      if(profileUserId) {
        fullUser = {};
        fullUser.profile = data;
        fullUser._id = apiName + "~" + profileUserId;
        fullUser.token = fakeUser.token;
        fullUser.tokenSecret = fakeUser.tokenSecret;

        if(logins[req.session.user.id]==undefined) {
          console.log("test 1: " + req.session.user.id);
          logins[req.session.user.id]={apiName : fullUser._id};
        }
        else {
          console.log("test 2: " + req.session.user.id);
          logins[req.session.user.id][apiName] = fullUser._id;
        }
      }
      done(null, fullUser);
    };
    handleRequest(apiName,fakeUser,options,callback);
  };

  if(appConfig[apiName].type == 'oauth-1.0') {
    // setup oauth1 through passport
    passport.use(apiName,
      new OAuthStrategy({
          userAuthorizationURL: appConfig[apiName].authorizationUrl,
          requestTokenURL: appConfig[apiName].requestTokenUrl,
          accessTokenURL: appConfig[apiName].accessTokenUrl,
          consumerKey: appConfig[apiName].clientId,
          consumerSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl,
          passReqToCallback: true
      }, strategyCallback
    ));
  }
  else if(appConfig[apiName].type == 'oauth-2.0') {
    /* TODO LATER
    // setup oauth2 through passport
    passport.use(apiName,
      new OAuth2Strategy({
          authorizationURL: appConfig[apiName].authorizationUrl,
          tokenURL: appConfig[apiName].accessTokenUrl,
          clientID: appConfig[apiName].clientId,
          clientSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl
      }, strategyCallback
    ));
    */
  }
  else {
    console.log(apiName + " has unknown auth type of " + appConfig[apiName].type);
  }
};
passportPlugin.register = register;

// simplified request handling
var handleRequest = function(apiName, user, options, callback) {
  console.log('handling request to ' + options.uri);

  passport._strategy(apiName)._oauth.get(appConfig[apiName].baseUrl+options.uri, user.token, user.tokenSecret, function (error, body, response) {
      if(error) {
        console.log('handle request error ' + JSON.stringify(error));
        callback(user,null);
        return;
      }

      if (response.statusCode === 200) {
        //console.log("RESULT=" + body);
        callback(user,JSON.parse(body));
      } else {
        console.log('handle request error ' + response.statusCode);
        callback(user,response.statusCode);
    }
  });
};
passportPlugin.handleRequest = handleRequest;

var auth = function(apiName,streams) {
  //console.log('auth attempt');
  passport.authenticate(apiName)(streams.req,streams.res,streams.next);
}
passportPlugin.auth = auth;

var authCallback = function(apiName,urls,streams) {
  //console.log('authCallback attempt');
  passport.authenticate(apiName,
                          {
                            'successRedirect' : urls.successUrl,
                            'failureRedirect' : urls.failureUrl
                          }
                       )(streams.req,streams.res,streams.next);
}
passportPlugin.authCallback = authCallback;

module.exports = passportPlugin;
<commit_msg>testing<commit_after>var appConfig = require('./passport-config'),
    passport = require('passport'),
    passportOauth = require('passport-oauth'),
    OAuthStrategy = passportOauth.OAuthStrategy,
    OAuth2Strategy = passportOauth.OAuth2Strategy,
    request = require('request');

var passportPlugin = {};

var logins = [];
var users = [];

var hasApi = function(apiName) {
  return appConfig[apiName] != undefined;
};
passportPlugin.hasApi = hasApi;

var findUser = function(apiName,req) {

console.log(JSON.stringify(logins));

  if(req.session.user == undefined
     || logins[req.session.user.id] == undefined) {
    return undefined;
  }

  return users[logins[req.session.user.id][apiName]];
};
passportPlugin.findUser = findUser;

var init = function(app) {

  app.use(passport.initialize());
  app.use(passport.session());

  // store user in persistent storage for passport
  passport.serializeUser(function(user, done) {
    //console.log('serialize user ' + user._id);
    users[user._id] = user;
    done(null, user._id);
  });

  // retrieve user from persistent store for passport
  passport.deserializeUser(function(id, done) {
    //console.log('deserialize user ' + id);
    var user = users[id];
    done(null, user);
  });

  // load all app configs
  for(var apiName in appConfig) {
    console.log('register = ' + apiName);
    register(apiName);
  }

};
passportPlugin.init = init;

var register = function(apiName) {

   var strategyCallback = function(req, token, tokenSecret, profile, done) {
    var fakeUser = { 'token' : token,
                     'tokenSecret' : tokenSecret };
    var options =  { 'method' : 'GET',
                     'uri' : appConfig[apiName].defaultLoadUrl };
    var callback = function(user, data) {
      var fullUser = null;

      //var profileUser = appConfig[apiName].userElementName?data[appConfig[apiName].userElementName]:data;
      var profileUserId = null;
      if(data) {
        var userIdPath = appConfig[apiName].userIdPath.split('.');
        var profileUser = data;
        for(var i=0;i<userIdPath.length;i++) {
          if(i==userIdPath.length-1) {
            profileUserId = profileUser[userIdPath[i]];
          }
          else {
            profileUser = profileUser[userIdPath[i]];
          }
        }
      }

      if(profileUserId) {
        fullUser = {};
        fullUser.profile = data;
        fullUser._id = apiName + "~" + profileUserId;
        fullUser.token = fakeUser.token;
        fullUser.tokenSecret = fakeUser.tokenSecret;

        if(logins[req.session.user.id]==undefined) {
          console.log("test 1: " + req.session.user.id);
          logins['test']={apiName : fullUser._id};
        }
        else {
          console.log("test 2: " + req.session.user.id);
          logins[req.session.user.id][apiName] = fullUser._id;
        }
      }
      done(null, fullUser);
    };
    handleRequest(apiName,fakeUser,options,callback);
  };

  if(appConfig[apiName].type == 'oauth-1.0') {
    // setup oauth1 through passport
    passport.use(apiName,
      new OAuthStrategy({
          userAuthorizationURL: appConfig[apiName].authorizationUrl,
          requestTokenURL: appConfig[apiName].requestTokenUrl,
          accessTokenURL: appConfig[apiName].accessTokenUrl,
          consumerKey: appConfig[apiName].clientId,
          consumerSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl,
          passReqToCallback: true
      }, strategyCallback
    ));
  }
  else if(appConfig[apiName].type == 'oauth-2.0') {
    /* TODO LATER
    // setup oauth2 through passport
    passport.use(apiName,
      new OAuth2Strategy({
          authorizationURL: appConfig[apiName].authorizationUrl,
          tokenURL: appConfig[apiName].accessTokenUrl,
          clientID: appConfig[apiName].clientId,
          clientSecret: appConfig[apiName].clientSecret,
          callbackURL: appConfig[apiName].callbackUrl
      }, strategyCallback
    ));
    */
  }
  else {
    console.log(apiName + " has unknown auth type of " + appConfig[apiName].type);
  }
};
passportPlugin.register = register;

// simplified request handling
var handleRequest = function(apiName, user, options, callback) {
  console.log('handling request to ' + options.uri);

  passport._strategy(apiName)._oauth.get(appConfig[apiName].baseUrl+options.uri, user.token, user.tokenSecret, function (error, body, response) {
      if(error) {
        console.log('handle request error ' + JSON.stringify(error));
        callback(user,null);
        return;
      }

      if (response.statusCode === 200) {
        //console.log("RESULT=" + body);
        callback(user,JSON.parse(body));
      } else {
        console.log('handle request error ' + response.statusCode);
        callback(user,response.statusCode);
    }
  });
};
passportPlugin.handleRequest = handleRequest;

var auth = function(apiName,streams) {
  //console.log('auth attempt');
  passport.authenticate(apiName)(streams.req,streams.res,streams.next);
}
passportPlugin.auth = auth;

var authCallback = function(apiName,urls,streams) {
  //console.log('authCallback attempt');
  passport.authenticate(apiName,
                          {
                            'successRedirect' : urls.successUrl,
                            'failureRedirect' : urls.failureUrl
                          }
                       )(streams.req,streams.res,streams.next);
}
passportPlugin.authCallback = authCallback;

module.exports = passportPlugin;
<|endoftext|>


<commit_before>/**
 * Created by andre on 1/07/17.
 */
const
    sendMessage = require('../tools/sendMessage'),
    PAUSED = 1,
    RESUME = 2
;

let event = function (userObject) {
    this.userObject = userObject;
};

event.prototype = {};

event.prototype.setDatastore = function (datastore) {
    this.datastore = datastore;
};

event.prototype.run = function () {
    function toDatastore (obj, nonIndexed) {
        nonIndexed = nonIndexed || [];
        const results = [];
        Object.keys(obj).forEach((k) => {
            if (obj[k] === undefined) {
                return;
            }
            results.push({
                name: k,
                value: obj[k],
                excludeFromIndexes: nonIndexed.indexOf(k) !== -1
            });
        });
        return results;
    }

    let action = null;

    if (this.userObject.restriction < 2 || typeof this.userObject.restriction !== "number") {
        sendMessage.sendTextMessage(this.userObject.mId, "¡Genial! En cualquier momento comenzarás a recibir mensajes con ofertas y promociones para ti", [], function () {},
            [{
                "type": "postback",
                "title": "Ver ofertas de hoy",
                "payload": "TODAY_PAYLOAD"
            }]);
        return;

    } else {
        this.userObject.restriction = 0;
        action = RESUME;
    }

    let key = this.datastore.key(["User", parseInt(this.userObject.id, 10)]);

    const entity = {
        key: key,
        data: toDatastore(this.userObject)
    };

    this.datastore.save(entity, (err) => {
        if (err) {
            console.error("Error saving datastore category preference");
            return;
        }

        sendMessage.sendTextMessage(this.userObject.mId, "¡Genial! En cualquier momento comenzarás a recibir mensajes con ofertas y promociones para ti", [], function () {},
            [{
                "type": "postback",
                "title": "Ver ofertas de hoy",
                "payload": "TODAY_PAYLOAD"
            }]);
    });
};

module.exports = event;
<commit_msg>Changed messages to bot-messages<commit_after>/**
 * Created by andre on 1/07/17.
 */
 let botMessages = require('../messages/bot-msgs');
const
    sendMessage = require('../tools/sendMessage'),
    PAUSED = 1,
    RESUME = 2
;

let event = function (userObject) {
    this.userObject = userObject;
};

event.prototype = {};

event.prototype.setDatastore = function (datastore) {
    this.datastore = datastore;
};

event.prototype.run = function () {
    function toDatastore (obj, nonIndexed) {
        nonIndexed = nonIndexed || [];
        const results = [];
        Object.keys(obj).forEach((k) => {
            if (obj[k] === undefined) {
                return;
            }
            results.push({
                name: k,
                value: obj[k],
                excludeFromIndexes: nonIndexed.indexOf(k) !== -1
            });
        });
        return results;
    }

    let action = null;

    if (this.userObject.restriction < 2 || typeof this.userObject.restriction !== "number") {
        sendMessage.sendTextMessage(this.userObject.mId, botMessages.START_SENDING_OFFERS, [], function () {},
            [{
                "type": "postback",
                "title": botMessages.START_SENDING_OFFERS_BUTTON1,
                "payload": "TODAY_PAYLOAD"
            },
            {
                "type": "postback",
                "title": botMessages.START_SENDING_OFFERS_BUTTON2,
                "payload": "SHARE_PAYLOAD"
            }]);
        return;

    } else {
        this.userObject.restriction = 0;
        action = RESUME;
    }

    let key = this.datastore.key(["User", parseInt(this.userObject.id, 10)]);

    const entity = {
        key: key,
        data: toDatastore(this.userObject)
    };

    this.datastore.save(entity, (err) => {
        if (err) {
            console.error("Error saving datastore category preference");
            return;
        }

        sendMessage.sendTextMessage(this.userObject.mId, botMessages.START_SENDING_OFFERS, [], function () {},
        [{
            "type": "postback",
            "title": botMessages.START_SENDING_OFFERS_BUTTON1,
            "payload": "TODAY_PAYLOAD"
        },
        {
            "type": "postback",
            "title": botMessages.START_SENDING_OFFERS_BUTTON2,
            "payload": "SHARE_PAYLOAD"
        }]);
    });
};

module.exports = event;
<|endoftext|>


<commit_before>import {exec as _exec} from "child_process";
import promisify from "es6-promisify";
import fs from "fs-promise";

const exec = promisify(_exec, function(err, res) {
	if (err) this.reject(err);
	else this.resolve(Array.isArray(res) ? res[0] : res);
});

export default async function(log, {package:pkg}) {
	await exec(`git tag -a v${pkg.version} -m ""`);
	await fs.writeFile("changelog.md", log, { flag: "a" });
	await exec(`git add changelog.md`);
	await exec(`git commit -m "chore: updated changelog for v${pkg.version}"`);
}
<commit_msg>fix (publish-changelog): don't commit, append to front<commit_after>import {exec as _exec} from "child_process";
import promisify from "es6-promisify";
import {readFile,writeFile} from "fs-promise";
import {resolve} from "path";

const exec = promisify(_exec, function(err, res) {
	if (err) this.reject(err);
	else this.resolve(Array.isArray(res) ? res[0] : res);
});

export default async function(log, {package:pkg,basedir="."}) {
	// tag the current commit with new version
	// done here because github variant tags and releases all-in-one
	await exec(`git tag -a v${pkg.version} -m ""`);

	// write the changelog to a file
	if (typeof log === "string") {
		let cfile = resolve(basedir, "changelog.md");
		let existing = "";
		try { existing = await readFile(cfile, "utf-8"); }
		catch(e) { if (e.code !== "ENOENT") throw e; }
		await writeFile(cfile, existing + log);
	}
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore');

/**
 * URI Helper
 * @type {Object}
 */
module.exports = {
    /**
     * Takes a resource object from an API configuration and returns the slug for that resource
     *
     * @param  {String} name Parameter name
     * @param  {String} uri  URI string with tokenized parameters
     * @return {Boolean}     True if parameter name is within the uri
     */
    isParameterNameInUri : function(name, uri)
    {
        var regex = new RegExp(':' + name + '(?![A-z])');

        return regex.test(uri);
    },

    /**
     * Injects value into tokenized URI
     *
     * @param  {String} name   Parameter name
     * @param  {String} uri    URI string with tokenized parameters
     * @param  {String} value  Parameter value
     * @return {String}        URI with replaced parameter
     */
    injectValueIntoUri : function(name, uri, value) {
        console.log(value);
        // inject array differently using name[]=value
        if (typeof value ==='object') {
            // skip if someone tries to put an object in a uri
            if (_.isArray(value)) {
                uri = uri.replace(new RegExp('(' + name + '=)'), '');
                var arrayRegex = new RegExp('(\:' + name + '(?![A-z]))');
                var params = [];
                for(var i in value) {
                    params.push(name + '[]=' + encodeURIComponent(value[i]));
                }
                uri = uri.replace(arrayRegex, params.join('&'));
            }
        } else {
            var regex = new RegExp(':' + name + '(?![A-z])');

            uri = uri.replace(regex, encodeURIComponent(value));
        }

        return uri;
    }
};
<commit_msg>Refs #88 - no debug<commit_after>'use strict';

var _ = require('underscore');

/**
 * URI Helper
 * @type {Object}
 */
module.exports = {
    /**
     * Takes a resource object from an API configuration and returns the slug for that resource
     *
     * @param  {String} name Parameter name
     * @param  {String} uri  URI string with tokenized parameters
     * @return {Boolean}     True if parameter name is within the uri
     */
    isParameterNameInUri : function(name, uri)
    {
        var regex = new RegExp(':' + name + '(?![A-z])');

        return regex.test(uri);
    },

    /**
     * Injects value into tokenized URI
     *
     * @param  {String} name   Parameter name
     * @param  {String} uri    URI string with tokenized parameters
     * @param  {String} value  Parameter value
     * @return {String}        URI with replaced parameter
     */
    injectValueIntoUri : function(name, uri, value) {
        // inject array differently using name[]=value
        if (typeof value ==='object') {
            // skip if someone tries to put an object in a uri
            if (_.isArray(value)) {
                uri = uri.replace(new RegExp('(' + name + '=)'), '');
                var arrayRegex = new RegExp('(\:' + name + '(?![A-z]))');
                var params = [];
                for(var i in value) {
                    params.push(name + '[]=' + encodeURIComponent(value[i]));
                }
                uri = uri.replace(arrayRegex, params.join('&'));
            }
        } else {
            var regex = new RegExp(':' + name + '(?![A-z])');

            uri = uri.replace(regex, encodeURIComponent(value));
        }

        return uri;
    }
};
<|endoftext|>


<commit_before>// ==========================================================================
//                        DG.DocumentArchiver
//
//  Author:   Kirk Swenson
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class

  Coordinating controller for the document.

  @extends SC.Object
*/

/*global CSV: true */

DG.DocumentArchiver = SC.Object.extend(
/** @scope DG.DocumentArchiver.prototype */ {

    /**
     Tests the JSON text for validity as a possible document.

     The following assertions are tested:

     (1) The document is valid JSON. that is: it parses correctly
     (2) The document looks like a valid CODAP document. It has all mandatory
     top level elements and no unexpected top level elements.
     (3) all internal links can be resolved.

     @param    {String}    iDocument -- The JSON-formatted document text
     @returns  {[String]}   An array of error messages, zero length, if none.
     */
    isValidJsonDocument: function (iDocument) {
      function visit(key, value, fn) {
        if (Array.isArray(value)) {
          value.forEach(function (item) {
            visit(key, item, fn);
          });
        } else if (typeof value === 'object') {
          DG.ObjectMap.forEach(value, function (key, item) {
            visit(key, item, fn);
          });
        } else {
          fn(key, value);
        }
      }

      function validateInternalRefs(iDocument) {
        var parts = subDocs.copy(false);
        var symbols = [];
        var references = [];
        parts.unshift(iDocument);
        parts.forEach(function (documentPart) {
          visit('doc', documentPart, function (key, value) {
            if (key === 'guid') {
              symbols.push(Number(value));
            } else if (key === 'id') {
              references.push(Number(value));
            }
          });
        });
        references.forEach(function (ref) {
          if (symbols.indexOf(Number(ref)) < 0) {
            errors.push('DG.AppController.validateDocument.unresolvedID'.loc(ref));
          }
        });
      }
      var expectedProperties = [
        'appBuildNum',
        'appName',
        'appVersion',
        'components',
        'contexts',
        'globalValues',
        'guid',
        'name',
        '_permissions'
      ];
      var requiredProperties = [
        'name'
      ];
      var errors = [];
      var doc;
      var subDocs = [];

      if (typeof iDocument === 'string') {
        try {
          doc = JSON.parse(iDocument);
        } catch (ex) {
          errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
        }
      } else {
        doc = iDocument;
      }

      if (DG.ExternalDocumentCache) {
        subDocs = DG.ExternalDocumentCache.fetchAll().map(function (iFragment) {
          var parsedFragment = {};
          if (typeof iDocument === 'string') {
            try {
              parsedFragment = JSON.parse(iFragment);
            } catch (ex) {
              errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
            }
          } else {
            parsedFragment = iFragment;
          }
          return parsedFragment;
        });
      }

      if (doc) {
        requiredProperties.forEach(function (prop) {
            if (!doc.hasOwnProperty(prop)) {
              errors.push('DG.AppController.validateDocument.missingRequiredProperty'.loc(prop));
            }
          }
        );
        DG.ObjectMap.keys(doc).forEach(function (prop) {
            if (expectedProperties.indexOf(prop) < 0) {
              errors.push('DG.AppController.validateDocument.unexpectedProperty'.loc(prop));
            }
          }
        );
        validateInternalRefs(doc);
      }
      DG.log('Document validation: ' + (errors.length? JSON.stringify(errors): 'No Errors'));
      return errors;
    },

  /**
    Open the specified document text as a new document, returning the newly-created document.
   */
  openDocument: function( iStore, iDocText) {
    var deferred = $.Deferred(),
    externalDocIds = DG.StringUtilities.scan(iDocText, /"externalDocumentId": ?"?(\d+)"?/g, function(m) { return m[1]; }),
        promises = DG.authorizationController.loadExternalDocuments(externalDocIds);

    Promise.all(promises).then(function() {
      try {
        var docArchive = SC.json.decode( iDocText),
          validationErrors;

        validationErrors = this.isValidJsonDocument(docArchive);
        if (validationErrors.length > 0) {
          deferred.reject(new Error('DG.AppController.validateDocument.invalidDocument'.loc(
            JSON.stringify(validationErrors))));
        }
        else {
          DG.store = DG.ModelStore.create();
          deferred.resolve(DG.Document.createDocument(docArchive));
        }
      } catch (ex) {
        deferred.reject(ex);
      }
      DG.ExternalDocumentCache.clear();
      DG.busyCursor.hide();
    }.bind(this));
    return deferred;
  },

  importCSV: function (iText, iFileName) {

    // trims empty columns from right side of
    function trimTrailingColumns(arr) {
      var newArr = [];
      arr.forEach(function (row) {
        var value;
        if (Array.isArray(row)) {
          do {
            value = row.pop();
          } while(value === '');
          if (value) row.push(value);
        }
        if (row.length) {
          newArr.push(row);
        }
      });
      return newArr;
    }
    function parseText() {
      var tValuesArray,
        tCollectionRow,
        tChildName = 'children',// Child Collection Name: should be first
                                // line of CSV
        tAttrNamesRow,// Column Header Names: should be second row
        tDoc = {
          name: 'DG.Document.defaultDocumentName'.loc(),
          components: [],
          contexts: [
            {
              "type": "DG.DataContext",
              "collections": [
                {
                  "attrs": [],
                  "cases": []
                }
              ],
              "contextStorage": {
                "gameUrl": iFileName
              }
            }
          ],
          appName: DG.APPNAME,
          appVersion: DG.VERSION,
          appBuildNum: DG.BUILD_NUM,
          globalValues: []
        },
        tAttrsArray = tDoc.contexts[0].collections[0].attrs,
        tCasesArray = tDoc.contexts[0].collections[0].cases;

      CSV.RELAXED = true;
      CSV.IGNORE_RECORD_LENGTH = true;
      tValuesArray = CSV.parse(iText);
      if (tValuesArray && tValuesArray.length >= 2) {
        tValuesArray = trimTrailingColumns(tValuesArray);
        tCollectionRow = tValuesArray.shift();
        tAttrNamesRow = tValuesArray[0];

        if (Array.isArray(tCollectionRow)) {
          // check if it looks like name row is missing
          if ((tAttrNamesRow.length === tCollectionRow.length) && (tAttrNamesRow.length > 1)) {
            tAttrNamesRow = tCollectionRow;
          } else {
            tChildName = tCollectionRow[0];
            tValuesArray.shift();
          }
        }
        else {
          tChildName = tCollectionRow;
          tValuesArray.shift();
        }
        tDoc.contexts[0].collections[0].name = tChildName;

        tAttrNamesRow.forEach(function (iName) {
          tAttrsArray.push( {
            name: iName
          });
        });

        tValuesArray.forEach( function( iValues) {
          var tCase = {
            values: {}
          };
          tAttrNamesRow.forEach( function( iName, iIndex) {
            tCase.values[ iName] = iValues[ iIndex];
          });
          tCasesArray.push( tCase);
        });

        return tDoc;
      }
    } // parseText

    var docArchive = parseText( );
    return docArchive;
  },
  /**
   *
   * @param iText - csv or tab-delimited
   * @returns {DG.Document}
   */
  importTextIntoDocument: function( iText, iName) {
    var docArchive = this.importCSV(iText, iName);

    return DG.Document.createDocument(docArchive);
  },

  /**
   *
   * @param iURL - URL of data interactive
   * @returns {DG.Document}
   */
  importURLIntoDocument: function (iURL) {
    var tDoc = {
      name: 'DG.Document.defaultDocumentName'.loc(),
      guid: 1,
      components: [
        {
          "type": "DG.GameView",
          "componentStorage": {
            "currentGameName": "",
            "currentGameUrl": iURL
          }
        }
      ],
      appName: DG.APPNAME,
      appVersion: DG.VERSION,
      appBuildNum: DG.BUILD_NUM,
      globalValues: []
    };

    return DG.Document.createDocument(tDoc);
  },

  /**
    Save the specified document in its JSON-text form.
    @param    {DG.Document}   iDocument   The document whose contents are to be archived

    signature of `callback`:
    @param  {Object} docArchive      An object suitable for JSON encoding
   */
  saveDocument: function( iDocument, callback, fullData) {

    // Prepare the context-specific storage for saving.
    // Start by saving the state of the current game in the appropriate context.
    // Callback below executes after the state has been saved
    DG.currDocumentController().saveCurrentGameState(function() {
      var docController = DG.currDocumentController();
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
        });
      if( docController) {
        // Prepare the component-specific storage for saving
        DG.ObjectMap.forEach( docController.componentControllersMap,
          function( iComponentID, iController) {
            iController.willSaveComponent();
          });
      }

      callback(iDocument.toArchive(fullData));
    });
  },

  saveDataContexts: function( iDocument, callback, saveAll) {
    var model,
        deferred = $.Deferred();
    DG.currDocumentController().saveCurrentGameState(function() {
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
          model = iContext.get('model');
          if ( saveAll || !SC.none(model.get('externalDocumentId'))) {
            callback(model, model.toArchive(true));
          }
        });
      deferred.resolve();
    });
    return deferred;
  }

});

<commit_msg>[Fixes #92564542] The check in importCSV/trimTrailingColumns for the presence of a value was being tripped up by the falseness of zero.<commit_after>// ==========================================================================
//                        DG.DocumentArchiver
//
//  Author:   Kirk Swenson
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class

  Coordinating controller for the document.

  @extends SC.Object
*/

/*global CSV: true */

DG.DocumentArchiver = SC.Object.extend(
/** @scope DG.DocumentArchiver.prototype */ {

    /**
     Tests the JSON text for validity as a possible document.

     The following assertions are tested:

     (1) The document is valid JSON. that is: it parses correctly
     (2) The document looks like a valid CODAP document. It has all mandatory
     top level elements and no unexpected top level elements.
     (3) all internal links can be resolved.

     @param    {String}    iDocument -- The JSON-formatted document text
     @returns  {[String]}   An array of error messages, zero length, if none.
     */
    isValidJsonDocument: function (iDocument) {
      function visit(key, value, fn) {
        if (Array.isArray(value)) {
          value.forEach(function (item) {
            visit(key, item, fn);
          });
        } else if (typeof value === 'object') {
          DG.ObjectMap.forEach(value, function (key, item) {
            visit(key, item, fn);
          });
        } else {
          fn(key, value);
        }
      }

      function validateInternalRefs(iDocument) {
        var parts = subDocs.copy(false);
        var symbols = [];
        var references = [];
        parts.unshift(iDocument);
        parts.forEach(function (documentPart) {
          visit('doc', documentPart, function (key, value) {
            if (key === 'guid') {
              symbols.push(Number(value));
            } else if (key === 'id') {
              references.push(Number(value));
            }
          });
        });
        references.forEach(function (ref) {
          if (symbols.indexOf(Number(ref)) < 0) {
            errors.push('DG.AppController.validateDocument.unresolvedID'.loc(ref));
          }
        });
      }
      var expectedProperties = [
        'appBuildNum',
        'appName',
        'appVersion',
        'components',
        'contexts',
        'globalValues',
        'guid',
        'name',
        '_permissions'
      ];
      var requiredProperties = [
        'name'
      ];
      var errors = [];
      var doc;
      var subDocs = [];

      if (typeof iDocument === 'string') {
        try {
          doc = JSON.parse(iDocument);
        } catch (ex) {
          errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
        }
      } else {
        doc = iDocument;
      }

      if (DG.ExternalDocumentCache) {
        subDocs = DG.ExternalDocumentCache.fetchAll().map(function (iFragment) {
          var parsedFragment = {};
          if (typeof iDocument === 'string') {
            try {
              parsedFragment = JSON.parse(iFragment);
            } catch (ex) {
              errors.push('DG.AppController.validateDocument.parseError'.loc(ex));
            }
          } else {
            parsedFragment = iFragment;
          }
          return parsedFragment;
        });
      }

      if (doc) {
        requiredProperties.forEach(function (prop) {
            if (!doc.hasOwnProperty(prop)) {
              errors.push('DG.AppController.validateDocument.missingRequiredProperty'.loc(prop));
            }
          }
        );
        DG.ObjectMap.keys(doc).forEach(function (prop) {
            if (expectedProperties.indexOf(prop) < 0) {
              errors.push('DG.AppController.validateDocument.unexpectedProperty'.loc(prop));
            }
          }
        );
        validateInternalRefs(doc);
      }
      DG.log('Document validation: ' + (errors.length? JSON.stringify(errors): 'No Errors'));
      return errors;
    },

  /**
    Open the specified document text as a new document, returning the newly-created document.
   */
  openDocument: function( iStore, iDocText) {
    var deferred = $.Deferred(),
    externalDocIds = DG.StringUtilities.scan(iDocText, /"externalDocumentId": ?"?(\d+)"?/g, function(m) { return m[1]; }),
        promises = DG.authorizationController.loadExternalDocuments(externalDocIds);

    Promise.all(promises).then(function() {
      try {
        var docArchive = SC.json.decode( iDocText),
          validationErrors;

        validationErrors = this.isValidJsonDocument(docArchive);
        if (validationErrors.length > 0) {
          deferred.reject(new Error('DG.AppController.validateDocument.invalidDocument'.loc(
            JSON.stringify(validationErrors))));
        }
        else {
          DG.store = DG.ModelStore.create();
          deferred.resolve(DG.Document.createDocument(docArchive));
        }
      } catch (ex) {
        deferred.reject(ex);
      }
      DG.ExternalDocumentCache.clear();
      DG.busyCursor.hide();
    }.bind(this));
    return deferred;
  },

  importCSV: function (iText, iFileName) {

    // trims empty columns from right side of
    function trimTrailingColumns(arr) {
      var newArr = [];
      arr.forEach(function (row) {
        var value;
        if (Array.isArray(row)) {
          do {
            value = row.pop();
          } while(value === '');
          if (!SC.empty(value)) row.push(value);
        }
        if (row.length) {
          newArr.push(row);
        }
      });
      return newArr;
    }
    function parseText() {
      var tValuesArray,
        tCollectionRow,
        tChildName = 'children',// Child Collection Name: should be first
                                // line of CSV
        tAttrNamesRow,// Column Header Names: should be second row
        tDoc = {
          name: 'DG.Document.defaultDocumentName'.loc(),
          components: [],
          contexts: [
            {
              "type": "DG.DataContext",
              "collections": [
                {
                  "attrs": [],
                  "cases": []
                }
              ],
              "contextStorage": {
                "gameUrl": iFileName
              }
            }
          ],
          appName: DG.APPNAME,
          appVersion: DG.VERSION,
          appBuildNum: DG.BUILD_NUM,
          globalValues: []
        },
        tAttrsArray = tDoc.contexts[0].collections[0].attrs,
        tCasesArray = tDoc.contexts[0].collections[0].cases;

      CSV.RELAXED = true;
      CSV.IGNORE_RECORD_LENGTH = true;
      tValuesArray = CSV.parse(iText);
      if (tValuesArray && tValuesArray.length >= 2) {
        tValuesArray = trimTrailingColumns(tValuesArray);
        tCollectionRow = tValuesArray.shift();
        tAttrNamesRow = tValuesArray[0];

        if (Array.isArray(tCollectionRow)) {
          // check if it looks like name row is missing
          if ((tAttrNamesRow.length === tCollectionRow.length) && (tAttrNamesRow.length > 1)) {
            tAttrNamesRow = tCollectionRow;
          } else {
            tChildName = tCollectionRow[0];
            tValuesArray.shift();
          }
        }
        else {
          tChildName = tCollectionRow;
          tValuesArray.shift();
        }
        tDoc.contexts[0].collections[0].name = tChildName;

        tAttrNamesRow.forEach(function (iName) {
          tAttrsArray.push( {
            name: iName
          });
        });

        tValuesArray.forEach( function( iValues) {
          var tCase = {
            values: {}
          };
          tAttrNamesRow.forEach( function( iName, iIndex) {
            tCase.values[ iName] = iValues[ iIndex];
          });
          tCasesArray.push( tCase);
        });

        return tDoc;
      }
    } // parseText

    var docArchive = parseText( );
    return docArchive;
  },
  /**
   *
   * @param iText - csv or tab-delimited
   * @returns {DG.Document}
   */
  importTextIntoDocument: function( iText, iName) {
    var docArchive = this.importCSV(iText, iName);

    return DG.Document.createDocument(docArchive);
  },

  /**
   *
   * @param iURL - URL of data interactive
   * @returns {DG.Document}
   */
  importURLIntoDocument: function (iURL) {
    var tDoc = {
      name: 'DG.Document.defaultDocumentName'.loc(),
      guid: 1,
      components: [
        {
          "type": "DG.GameView",
          "componentStorage": {
            "currentGameName": "",
            "currentGameUrl": iURL
          }
        }
      ],
      appName: DG.APPNAME,
      appVersion: DG.VERSION,
      appBuildNum: DG.BUILD_NUM,
      globalValues: []
    };

    return DG.Document.createDocument(tDoc);
  },

  /**
    Save the specified document in its JSON-text form.
    @param    {DG.Document}   iDocument   The document whose contents are to be archived

    signature of `callback`:
    @param  {Object} docArchive      An object suitable for JSON encoding
   */
  saveDocument: function( iDocument, callback, fullData) {

    // Prepare the context-specific storage for saving.
    // Start by saving the state of the current game in the appropriate context.
    // Callback below executes after the state has been saved
    DG.currDocumentController().saveCurrentGameState(function() {
      var docController = DG.currDocumentController();
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
        });
      if( docController) {
        // Prepare the component-specific storage for saving
        DG.ObjectMap.forEach( docController.componentControllersMap,
          function( iComponentID, iController) {
            iController.willSaveComponent();
          });
      }

      callback(iDocument.toArchive(fullData));
    });
  },

  saveDataContexts: function( iDocument, callback, saveAll) {
    var model,
        deferred = $.Deferred();
    DG.currDocumentController().saveCurrentGameState(function() {
      DG.DataContext.forEachContextInMap( iDocument.get('id'),
        function( iContextID, iContext) {
          iContext.willSaveContext();
          model = iContext.get('model');
          if ( saveAll || !SC.none(model.get('externalDocumentId'))) {
            callback(model, model.toArchive(true));
          }
        });
      deferred.resolve();
    });
    return deferred;
  }

});

<|endoftext|>