<commit_before>/*
 *
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * 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 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * 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
 *
 */

/**
 *  Links.js
 *
 *  Created by Julia Radzhabova on 22.12.2017
 *  Copyright (c) 2018 Ascensio System SIA. All rights reserved.
 *
 */

define([
    'core',
    'documenteditor/main/app/view/Links',
    'documenteditor/main/app/view/NoteSettingsDialog',
    'documenteditor/main/app/view/HyperlinkSettingsDialog',
    'documenteditor/main/app/view/TableOfContentsSettings',
    'documenteditor/main/app/view/BookmarksDialog',
    'documenteditor/main/app/view/CaptionDialog'
], function () {
    'use strict';

    DE.Controllers.Links = Backbone.Controller.extend(_.extend({
        models : [],
        collections : [
        ],
        views : [
            'Links'
        ],
        sdkViewName : '#id_main',

        initialize: function () {

            this.addListeners({
                'Links': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate,
                    'links:notes': this.onNotesClick,
                    'links:hyperlink': this.onHyperlinkClick,
                    'links:bookmarks': this.onBookmarksClick,
                    'links:caption': this.onCaptionClick
                },
                'DocumentHolder': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate
                }
            });
        },
        onLaunch: function () {
            this._state = {
                prcontrolsdisable:undefined,
                in_object: false
            };
            Common.Gateway.on('setactionlink', function (url) {
                console.log('url with actions: ' + url);
            }.bind(this));
        },

        setApi: function (api) {
            if (api) {
                this.api = api;
                this.api.asc_registerCallback('asc_onFocusObject', this.onApiFocusObject.bind(this));
                this.api.asc_registerCallback('asc_onCanAddHyperlink',      _.bind(this.onApiCanAddHyperlink, this));
                this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.onCoAuthoringDisconnect, this));
                Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this));
                this.api.asc_registerCallback('asc_onShowContentControlsActions',_.bind(this.onShowContentControlsActions, this));
                this.api.asc_registerCallback('asc_onHideContentControlsActions',_.bind(this.onHideContentControlsActions, this));
            }
            return this;
        },

        setConfig: function(config) {
            this.toolbar = config.toolbar;
            this.view = this.createView('Links', {
                toolbar: this.toolbar.toolbar
            });
        },

        SetDisabled: function(state) {
            this.view && this.view.SetDisabled(state);
        },

        getView: function(name) {
            return !name && this.view ?
                this.view : Backbone.Controller.prototype.getView.call(this, name);
        },

        onCoAuthoringDisconnect: function() {
            this.SetDisabled(true);
        },

        onApiFocusObject: function(selectedObjects) {
            if (!this.toolbar.editMode) return;

            var pr, i = -1, type,
                paragraph_locked = false,
                header_locked = false,
                in_header = false,
                in_equation = false,
                in_image = false,
                in_table = false,
                frame_pr = null;

            while (++i < selectedObjects.length) {
                type = selectedObjects[i].get_ObjectType();
                pr   = selectedObjects[i].get_ObjectValue();

                if (type === Asc.c_oAscTypeSelectElement.Paragraph) {
                    paragraph_locked = pr.get_Locked();
                    frame_pr = pr;
                } else if (type === Asc.c_oAscTypeSelectElement.Header) {
                    header_locked = pr.get_Locked();
                    in_header = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Image) {
                    in_image = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Math) {
                    in_equation = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Table) {
                    in_table = true;
                }
            }
            this._state.prcontrolsdisable = paragraph_locked || header_locked;
            this._state.in_object = in_image || in_table || in_equation;

            var control_props = this.api.asc_IsContentControl() ? this.api.asc_GetContentControlProperties() : null,
                control_plain = (control_props) ? (control_props.get_ContentControlType()==Asc.c_oAscSdtLevelType.Inline) : false;
            var rich_del_lock = (frame_pr) ? !frame_pr.can_DeleteBlockContentControl() : true,
                rich_edit_lock = (frame_pr) ? !frame_pr.can_EditBlockContentControl() : true,
                plain_del_lock = (frame_pr) ? !frame_pr.can_DeleteInlineContentControl() : true,
                plain_edit_lock = (frame_pr) ? !frame_pr.can_EditInlineContentControl() : true;

            var need_disable = paragraph_locked || in_equation || in_image || in_header || control_plain || rich_edit_lock || plain_edit_lock;
            this.view.btnsNotes.setDisabled(need_disable);

            need_disable = paragraph_locked || header_locked || in_header || control_plain;
            this.view.btnBookmarks.setDisabled(need_disable);

            need_disable = in_header || rich_edit_lock || plain_edit_lock || rich_del_lock || plain_del_lock;
            this.view.btnsContents.setDisabled(need_disable);
        },

        onApiCanAddHyperlink: function(value) {
            this.toolbar.editMode && this.view.btnsHyperlink.setDisabled(!value || this._state.prcontrolsdisable);
        },

        onHyperlinkClick: function(btn) {
            var me = this,
                win, props, text;

            if (me.api){

                var handlerDlg = function(dlg, result) {
                    if (result == 'ok') {
                        props = dlg.getSettings();
                        (text!==false)
                            ? me.api.add_Hyperlink(props)
                            : me.api.change_Hyperlink(props);
                    }

                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                };

                text = me.api.can_AddHyperlink();

                if (text !== false) {
                    win = new DE.Views.HyperlinkSettingsDialog({
                        api: me.api,
                        handler: handlerDlg
                    });

                    props = new Asc.CHyperlinkProperty();
                    props.put_Text(text);

                    win.show();
                    win.setSettings(props);
                } else {
                    var selectedElements = me.api.getSelectedElements();
                    if (selectedElements && _.isArray(selectedElements)){
                        _.each(selectedElements, function(el, i) {
                            if (selectedElements[i].get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink)
                                props = selectedElements[i].get_ObjectValue();
                        });
                    }
                    if (props) {
                        win = new DE.Views.HyperlinkSettingsDialog({
                            api: me.api,
                            handler: handlerDlg
                        });
                        win.show();
                        win.setSettings(props);
                    }
                }
            }

            Common.component.Analytics.trackEvent('ToolBar', 'Add Hyperlink');
        },

        onTableContents: function(type, currentTOC){
            currentTOC = !!currentTOC;
            var props = this.api.asc_GetTableOfContentsPr(currentTOC);
            switch (type) {
                case 0:
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(true);
                    props.put_RightAlignTab(true);
                    props.put_TabLeader( Asc.c_oAscTabLeader.Dot);
                    (currentTOC) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(null, props);
                    break;
                case 1:
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(false);
                    props.put_TabLeader( Asc.c_oAscTabLeader.None);
                    (currentTOC) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(null, props);
                    break;
                case 'settings':
                    var me = this,
                        win = new DE.Views.TableOfContentsSettings({
                        api: this.api,
                        props: props,
                        handler: function(result, value) {
                            if (result == 'ok') {
                                (props) ? me.api.asc_SetTableOfContentsPr(value) : me.api.asc_AddTableOfContents(null, value);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        }
                    });
                    win.show();
                    break;
                case 'remove':
                    currentTOC = (currentTOC && props) ? props.get_InternalClass() : undefined;
                    this.api.asc_RemoveTableOfContents(currentTOC);
                    break;
            }
            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
        },

        onTableContentsUpdate: function(type, currentTOC){
            var props = this.api.asc_GetTableOfContentsPr(currentTOC);
            if (props) {
                if (currentTOC && props)
                    currentTOC = props.get_InternalClass();
                this.api.asc_UpdateTableOfContents(type == 'pages', currentTOC);
            }
            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
        },

        onNotesClick: function(type) {
            var me = this;
            switch (type) {
                case 'ins_footnote':
                    this.api.asc_AddFootnote();
                    break;
                case 'delele':
                    Common.UI.warning({
                        msg: this.view.confirmDeleteFootnotes,
                        buttons: ['yes', 'no'],
                        primary: 'yes',
                        callback: _.bind(function (btn) {
                            if (btn == 'yes') {
                                this.api.asc_RemoveAllFootnotes();
                            }
                            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
                        }, this)
                    });
                    break;
                case 'settings':
                    (new DE.Views.NoteSettingsDialog({
                        api: me.api,
                        handler: function (result, settings) {
                            if (settings) {
                                me.api.asc_SetFootnoteProps(settings.props, settings.applyToAll);
                                if (result == 'insert')
                                    setTimeout(function() {
                                        me.api.asc_AddFootnote(settings.custom);
                                    }, 1);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        },
                        props: me.api.asc_GetFootnoteProps()
                    })).show();
                    break;
                case 'prev':
                    this.api.asc_GotoFootnote(false);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
                case 'next':
                    this.api.asc_GotoFootnote(true);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
            }
        },

        onBookmarksClick: function(btn) {
            var me = this;
            (new DE.Views.BookmarksDialog({
                api: me.api,
                appOptions: me.toolbar.appOptions,
                props: me.api.asc_GetBookmarksManager(),
                handler: function (result, settings) {
                    if (settings) {
                    }
                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                }
            })).show();
        },

        onCaptionClick: function(btn) {
            var me = this;
            (new DE.Views.CaptionDialog({
                isObject: this._state.in_object,
                handler: function (result, settings) {
                    if (result == 'ok') {
                        me.api.asc_AddObjectCaption(settings);
                    }
                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                }
            })).show();
        },

        onShowTOCActions: function(obj, x, y) {
            var action = obj.button,
                menu = (action==AscCommon.CCButtonType.Toc) ? this.view.contentsUpdateMenu : this.view.contentsMenu,
                documentHolderView  = this.getApplication().getController('DocumentHolder').documentHolder,
                menuContainer = documentHolderView.cmpEl.find(Common.Utils.String.format('#menu-container-{0}', menu.id)),
                me = this;

            if (!menu) return;
            this._fromShowContentControls = true;
            Common.UI.Menu.Manager.hideAll();

            if (!menu.rendered) {
                // Prepare menu container
                if (menuContainer.length < 1) {
                    menuContainer = $(Common.Utils.String.format('

', menu.id));
                    documentHolderView.cmpEl.append(menuContainer);
                }

                menu.render(menuContainer);
                menu.cmpEl.attr({tabindex: "-1"});
                menu.on('hide:after', function(){
                    if (!me._fromShowContentControls)
                        me.api.asc_UncheckContentControlButtons();
                });
            }

            menuContainer.css({left: x, top : y});
            menuContainer.attr('data-value', 'prevent-canvas-click');
            documentHolderView._preventClick = true;
            menu.show();

            menu.alignPosition();
            _.delay(function() {
                menu.cmpEl.focus();
            }, 10);
            this._fromShowContentControls = false;
        },

        onHideContentControlsActions: function() {
            this.view.contentsMenu && this.view.contentsMenu.hide();
            this.view.contentsUpdateMenu && this.view.contentsUpdateMenu.hide();
        },

        onShowContentControlsActions: function(obj, x, y) {
            (obj.type == Asc.c_oAscContentControlSpecificType.TOC) && this.onShowTOCActions(obj, x, y);
        }

    }, DE.Controllers.Links || {}));
});<commit_msg>[DE] Fix Bug 43980, Bug 40362<commit_after>/*
 *
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * 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 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * 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
 *
 */

/**
 *  Links.js
 *
 *  Created by Julia Radzhabova on 22.12.2017
 *  Copyright (c) 2018 Ascensio System SIA. All rights reserved.
 *
 */

define([
    'core',
    'documenteditor/main/app/view/Links',
    'documenteditor/main/app/view/NoteSettingsDialog',
    'documenteditor/main/app/view/HyperlinkSettingsDialog',
    'documenteditor/main/app/view/TableOfContentsSettings',
    'documenteditor/main/app/view/BookmarksDialog',
    'documenteditor/main/app/view/CaptionDialog'
], function () {
    'use strict';

    DE.Controllers.Links = Backbone.Controller.extend(_.extend({
        models : [],
        collections : [
        ],
        views : [
            'Links'
        ],
        sdkViewName : '#id_main',

        initialize: function () {

            this.addListeners({
                'Links': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate,
                    'links:notes': this.onNotesClick,
                    'links:hyperlink': this.onHyperlinkClick,
                    'links:bookmarks': this.onBookmarksClick,
                    'links:caption': this.onCaptionClick
                },
                'DocumentHolder': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate
                }
            });
        },
        onLaunch: function () {
            this._state = {
                prcontrolsdisable:undefined,
                in_object: false
            };
            Common.Gateway.on('setactionlink', function (url) {
                console.log('url with actions: ' + url);
            }.bind(this));
        },

        setApi: function (api) {
            if (api) {
                this.api = api;
                this.api.asc_registerCallback('asc_onFocusObject', this.onApiFocusObject.bind(this));
                this.api.asc_registerCallback('asc_onCanAddHyperlink',      _.bind(this.onApiCanAddHyperlink, this));
                this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.onCoAuthoringDisconnect, this));
                Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this));
                this.api.asc_registerCallback('asc_onShowContentControlsActions',_.bind(this.onShowContentControlsActions, this));
                this.api.asc_registerCallback('asc_onHideContentControlsActions',_.bind(this.onHideContentControlsActions, this));
            }
            return this;
        },

        setConfig: function(config) {
            this.toolbar = config.toolbar;
            this.view = this.createView('Links', {
                toolbar: this.toolbar.toolbar
            });
        },

        SetDisabled: function(state) {
            this.view && this.view.SetDisabled(state);
        },

        getView: function(name) {
            return !name && this.view ?
                this.view : Backbone.Controller.prototype.getView.call(this, name);
        },

        onCoAuthoringDisconnect: function() {
            this.SetDisabled(true);
        },

        onApiFocusObject: function(selectedObjects) {
            if (!this.toolbar.editMode) return;

            var pr, i = -1, type,
                paragraph_locked = false,
                header_locked = false,
                in_header = false,
                in_equation = false,
                in_image = false,
                in_table = false,
                frame_pr = null;

            while (++i < selectedObjects.length) {
                type = selectedObjects[i].get_ObjectType();
                pr   = selectedObjects[i].get_ObjectValue();

                if (type === Asc.c_oAscTypeSelectElement.Paragraph) {
                    paragraph_locked = pr.get_Locked();
                    frame_pr = pr;
                } else if (type === Asc.c_oAscTypeSelectElement.Header) {
                    header_locked = pr.get_Locked();
                    in_header = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Image) {
                    in_image = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Math) {
                    in_equation = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Table) {
                    in_table = true;
                }
            }
            this._state.prcontrolsdisable = paragraph_locked || header_locked;
            this._state.in_object = in_image || in_table || in_equation;

            var control_props = this.api.asc_IsContentControl() ? this.api.asc_GetContentControlProperties() : null,
                control_plain = (control_props) ? (control_props.get_ContentControlType()==Asc.c_oAscSdtLevelType.Inline) : false;
            var rich_del_lock = (frame_pr) ? !frame_pr.can_DeleteBlockContentControl() : false,
                rich_edit_lock = (frame_pr) ? !frame_pr.can_EditBlockContentControl() : false,
                plain_del_lock = (frame_pr) ? !frame_pr.can_DeleteInlineContentControl() : false,
                plain_edit_lock = (frame_pr) ? !frame_pr.can_EditInlineContentControl() : false;

            var need_disable = paragraph_locked || in_equation || in_image || in_header || control_plain || rich_edit_lock || plain_edit_lock;
            this.view.btnsNotes.setDisabled(need_disable);

            need_disable = paragraph_locked || header_locked || in_header || control_plain;
            this.view.btnBookmarks.setDisabled(need_disable);

            need_disable = in_header || rich_edit_lock || plain_edit_lock || rich_del_lock || plain_del_lock;
            this.view.btnsContents.setDisabled(need_disable);
        },

        onApiCanAddHyperlink: function(value) {
            this.toolbar.editMode && this.view.btnsHyperlink.setDisabled(!value || this._state.prcontrolsdisable);
        },

        onHyperlinkClick: function(btn) {
            var me = this,
                win, props, text;

            if (me.api){

                var handlerDlg = function(dlg, result) {
                    if (result == 'ok') {
                        props = dlg.getSettings();
                        (text!==false)
                            ? me.api.add_Hyperlink(props)
                            : me.api.change_Hyperlink(props);
                    }

                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                };

                text = me.api.can_AddHyperlink();

                if (text !== false) {
                    win = new DE.Views.HyperlinkSettingsDialog({
                        api: me.api,
                        handler: handlerDlg
                    });

                    props = new Asc.CHyperlinkProperty();
                    props.put_Text(text);

                    win.show();
                    win.setSettings(props);
                } else {
                    var selectedElements = me.api.getSelectedElements();
                    if (selectedElements && _.isArray(selectedElements)){
                        _.each(selectedElements, function(el, i) {
                            if (selectedElements[i].get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink)
                                props = selectedElements[i].get_ObjectValue();
                        });
                    }
                    if (props) {
                        win = new DE.Views.HyperlinkSettingsDialog({
                            api: me.api,
                            handler: handlerDlg
                        });
                        win.show();
                        win.setSettings(props);
                    }
                }
            }

            Common.component.Analytics.trackEvent('ToolBar', 'Add Hyperlink');
        },

        onTableContents: function(type, currentTOC){
            currentTOC = !!currentTOC;
            var props = this.api.asc_GetTableOfContentsPr(currentTOC);
            switch (type) {
                case 0:
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(true);
                    props.put_RightAlignTab(true);
                    props.put_TabLeader( Asc.c_oAscTabLeader.Dot);
                    (currentTOC) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(null, props);
                    break;
                case 1:
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(false);
                    props.put_TabLeader( Asc.c_oAscTabLeader.None);
                    (currentTOC) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(null, props);
                    break;
                case 'settings':
                    var me = this,
                        win = new DE.Views.TableOfContentsSettings({
                        api: this.api,
                        props: props,
                        handler: function(result, value) {
                            if (result == 'ok') {
                                (props) ? me.api.asc_SetTableOfContentsPr(value) : me.api.asc_AddTableOfContents(null, value);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        }
                    });
                    win.show();
                    break;
                case 'remove':
                    currentTOC = (currentTOC && props) ? props.get_InternalClass() : undefined;
                    this.api.asc_RemoveTableOfContents(currentTOC);
                    break;
            }
            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
        },

        onTableContentsUpdate: function(type, currentTOC){
            var props = this.api.asc_GetTableOfContentsPr(currentTOC);
            if (props) {
                if (currentTOC && props)
                    currentTOC = props.get_InternalClass();
                this.api.asc_UpdateTableOfContents(type == 'pages', currentTOC);
            }
            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
        },

        onNotesClick: function(type) {
            var me = this;
            switch (type) {
                case 'ins_footnote':
                    this.api.asc_AddFootnote();
                    break;
                case 'delele':
                    Common.UI.warning({
                        msg: this.view.confirmDeleteFootnotes,
                        buttons: ['yes', 'no'],
                        primary: 'yes',
                        callback: _.bind(function (btn) {
                            if (btn == 'yes') {
                                this.api.asc_RemoveAllFootnotes();
                            }
                            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
                        }, this)
                    });
                    break;
                case 'settings':
                    (new DE.Views.NoteSettingsDialog({
                        api: me.api,
                        handler: function (result, settings) {
                            if (settings) {
                                me.api.asc_SetFootnoteProps(settings.props, settings.applyToAll);
                                if (result == 'insert')
                                    setTimeout(function() {
                                        me.api.asc_AddFootnote(settings.custom);
                                    }, 1);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        },
                        props: me.api.asc_GetFootnoteProps()
                    })).show();
                    break;
                case 'prev':
                    this.api.asc_GotoFootnote(false);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
                case 'next':
                    this.api.asc_GotoFootnote(true);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
            }
        },

        onBookmarksClick: function(btn) {
            var me = this;
            (new DE.Views.BookmarksDialog({
                api: me.api,
                appOptions: me.toolbar.appOptions,
                props: me.api.asc_GetBookmarksManager(),
                handler: function (result, settings) {
                    if (settings) {
                    }
                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                }
            })).show();
        },

        onCaptionClick: function(btn) {
            var me = this;
            (new DE.Views.CaptionDialog({
                isObject: this._state.in_object,
                handler: function (result, settings) {
                    if (result == 'ok') {
                        me.api.asc_AddObjectCaption(settings);
                    }
                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                }
            })).show();
        },

        onShowTOCActions: function(obj, x, y) {
            var action = obj.button,
                menu = (action==AscCommon.CCButtonType.Toc) ? this.view.contentsUpdateMenu : this.view.contentsMenu,
                documentHolderView  = this.getApplication().getController('DocumentHolder').documentHolder,
                menuContainer = documentHolderView.cmpEl.find(Common.Utils.String.format('#menu-container-{0}', menu.id)),
                me = this;

            if (!menu) return;
            this._fromShowContentControls = true;
            Common.UI.Menu.Manager.hideAll();

            if (!menu.rendered) {
                // Prepare menu container
                if (menuContainer.length < 1) {
                    menuContainer = $(Common.Utils.String.format('

', menu.id));
                    documentHolderView.cmpEl.append(menuContainer);
                }

                menu.render(menuContainer);
                menu.cmpEl.attr({tabindex: "-1"});
                menu.on('hide:after', function(){
                    if (!me._fromShowContentControls)
                        me.api.asc_UncheckContentControlButtons();
                });
            }

            menuContainer.css({left: x, top : y});
            menuContainer.attr('data-value', 'prevent-canvas-click');
            documentHolderView._preventClick = true;
            menu.show();

            menu.alignPosition();
            _.delay(function() {
                menu.cmpEl.focus();
            }, 10);
            this._fromShowContentControls = false;
        },

        onHideContentControlsActions: function() {
            this.view.contentsMenu && this.view.contentsMenu.hide();
            this.view.contentsUpdateMenu && this.view.contentsUpdateMenu.hide();
        },

        onShowContentControlsActions: function(obj, x, y) {
            (obj.type == Asc.c_oAscContentControlSpecificType.TOC) && this.onShowTOCActions(obj, x, y);
        }

    }, DE.Controllers.Links || {}));
});<|endoftext|>


<commit_before>var wingedMonkeyDirectives = angular.module('wingedMonkeyDirectives',[]);

// Toggle directive
wingedMonkeyDirectives.directive('wmToggle', function () {
  return function (scope, elm, attrs) {
    scope.$watch(attrs.wmToggle, function (newVal, oldVal) {
      if (newVal) {
        elm.removeClass('js_hide').addClass('js_show');
      } else {
        elm.removeClass('js_show').addClass('js_hide');
      }
    });
  };
});

wingedMonkeyDirectives.directive('wmProviderAppConfirm', function() {
  return{
    restrict: "A",
    replace: true,
    scope: {
      message: '@wmProviderAppConfirm',
      providerApp: '=',
      action: '@',
      confirmAction: '&',
      confirmTitle: '@',
      cancel: '@'
    },
    templateUrl: "assets/wm_provider_app_confirm.html",
    link: function(scope, element, attrs) {

      var initialButton = angular.element(element.children()[0]);
      var message = angular.element(element.children()[1]);
      var confirmButton = angular.element(message.children()[1]);
      var cancelButton = angular.element(message.children()[2]);

      initialButton.bind('click', open);
      confirmButton.bind('click', close);
      cancelButton.bind('click', close);

      function open() {
        message.addClass('js_show');
        message.removeClass('js_hide');
        initialButton.addClass('active');
        initialButton.attr("disabled", "true");
      }

      function close() {
        message.addClass('js_hide');
        message.removeClass('js_show');
        initialButton.removeClass('active');
        initialButton.removeAttr("disabled");
      }
    }
  }
});
<commit_msg>Updated confirm message directive<commit_after>var wingedMonkeyDirectives = angular.module('wingedMonkeyDirectives',[]);

// Toggle directive
wingedMonkeyDirectives.directive('wmToggle', function () {
  return function (scope, elm, attrs) {
    scope.$watch(attrs.wmToggle, function (newVal, oldVal) {
      if (newVal) {
        elm.removeClass('js_hide').addClass('js_show');
      } else {
        elm.removeClass('js_show').addClass('js_hide');
      }
    });
  };
});

wingedMonkeyDirectives.directive('wmProviderAppConfirm', function() {
  return{
    restrict: "A",
    replace: true,
    scope: {
      message: '@wmProviderAppConfirm',
      providerApp: '=',
      action: '@',
      confirmAction: '&',
      confirmTitle: '@',
      cancel: '@'
    },
    templateUrl: "assets/wm_provider_app_confirm.html",
    link: function(scope, element, attrs) {

      var initialButton = angular.element(element.children()[0]);
      var confirm_message = angular.element(element.children()[1]);
      var confirmButton = angular.element(angular.element(confirm_message.children()[1]).children()[0]);
      var cancelButton = angular.element(angular.element(confirm_message.children()[1]).children()[1]);

      initialButton.bind('click', open);
      confirmButton.bind('click', close);
      cancelButton.bind('click', cancel);

      function open() {
        confirm_message.addClass('js_show');
        confirm_message.removeClass('js_hide');
        initialButton.addClass('active');
        initialButton.attr("disabled", "true");
      }

      function close() {
        confirm_message.addClass('js_hide');
        confirm_message.removeClass('js_show');
        initialButton.removeClass('active');
      }

      function cancel() {
        close();
        initialButton.removeAttr("disabled");
      }
    }
  }
});
<|endoftext|>


<commit_before>(function(name, definition) {
    if (typeof module !== 'undefined') { module.exports = definition(); }
    else if (typeof define === 'function' && typeof define.amd === 'object') { define(definition); }
    else  { this[name] = definition(); }
}('MediaTag', function() {
    var cache;
    var cypherChunkLength = 131088;

    // Save a blob on the file system
    var saveFile = function (blob, url, fileName) {
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveOrOpenBlob(blob, fileName);
        } else {
            // We want to be able to download the file with a name, so we need an "a" tag with
            // a download attribute
            var a = document.createElement("a");
            a.href = url;
            a.download = fileName;
            // It's not in the DOM, so we can't use a.click();
            var event = new MouseEvent("click");
            a.dispatchEvent(event);
        }
    };

    var fixHTML = function (str) {
        if (!str) { return ''; }
        return str.replace(/[<>&"']/g, function (x) {
            return ({ "<": "<", ">": "&gt", "&": "&", '"': """, "'": "'" })[x];
        });
    };

    var isplainTextFile = function (metadata) {
        // does its type begins with "text/"
        if (metadata.type.indexOf("text/") === 0) { return true; }
        // no type and no file extension -> let's guess it's plain text
        var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.name) || [];
        if (!metadata.type && !parsedName[2]) { return true; }
        // other exceptions
        if (metadata.type === 'application/x-javascript') { return true; }
        if (metadata.type === 'application/xml') { return true; }
        return false;
    };

    // Default config, can be overriden per media-tag call
    var config = {
        allowed: [
            'text/plain',
            'image/png',
            'image/jpeg',
            'image/jpg',
            'image/gif',
            'audio/mp3',
            'audio/ogg',
            'audio/wav',
            'audio/webm',
            'video/mp4',
            'video/ogg',
            'video/webm',
            'application/pdf',
            //'application/dash+xml', // FIXME?
            'download'
        ],
        pdf: {},
        download: {
            text: "Download"
        },
        Plugins: {
            /**
             * @param {object}   metadataObject {name,  metadatatype, owners} containing metadata of the file
             * @param {strint}   url     Url of the blob object
             * @param {Blob}     content Blob object containing the data of the file
             * @param {object}   cfg     Object {Plugins, allowed, download, pdf} containing infos about plugins
             * @param {function} cb      Callback function: (err, pluginElement) => {}
             */
            text: function (metadata, url, content, cfg, cb) {
                var plainText = document.createElement('div');
                plainText.className = "plain-text-reader";
                plainText.setAttribute('style', 'white-space: pre-wrap;');
                var reader = new FileReader();
                reader.addEventListener('loadend', function (e) {
                    plainText.innerText = e.srcElement.result;
                    cb(void 0, plainText);
                });
                reader.readAsText(content);
            },
            image: function (metadata, url, content, cfg, cb) {
                var img = document.createElement('img');
                img.setAttribute('src', url);
                img.blob = content;
                cb(void 0, img);
            },
            video: function (metadata, url, content, cfg, cb) {
                var video = document.createElement('video');
                video.setAttribute('src', url);
                video.setAttribute('controls', true);
                cb(void 0, video);
            },
            audio: function (metadata, url, content, cfg, cb) {
                var audio = document.createElement('audio');
                audio.setAttribute('src', url);
                audio.setAttribute('controls', true);
                cb(void 0, audio);
            },
            pdf: function (metadata, url, content, cfg, cb) {
                var iframe = document.createElement('iframe');
                if (cfg.pdf.viewer) { // PDFJS
                    var viewerUrl = cfg.pdf.viewer + '?file=' + url;
                    iframe.src = viewerUrl + '#' + window.encodeURIComponent(metadata.name);
                    return void cb (void 0, iframe);
                }
                iframe.src = url + '#' + window.encodeURIComponent(metadata.name);
                return void cb (void 0, iframe);
            },
            download: function (metadata, url, content, cfg, cb) {
                var btn = document.createElement('button');
                btn.setAttribute('class', 'btn btn-success');
                btn.innerHTML = cfg.download.text + '
' +
                                (metadata.name ? '' + fixHTML(metadata.name) + '' : '');
                btn.addEventListener('click', function () {
                    saveFile(content, url, metadata.name);
                });
                cb(void 0, btn);
            }
        }
    };

    // Download a blob from href
    var download = function (src, _cb) {
        var cb = function (e, res) {
            _cb(e, res);
            cb = function () {};
        };

        var xhr = new XMLHttpRequest();
        xhr.open('GET', src, true);
        xhr.responseType = 'arraybuffer';

        xhr.onerror = function () { return void cb("XHR_ERROR"); };
        xhr.onload = function () {
            // Error?
            if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }

            var arrayBuffer = xhr.response;
            if (arrayBuffer) { cb(null, new Uint8Array(arrayBuffer)); }
        };

        xhr.send(null);
    };

    // Decryption tools
    var Decrypt = {
        // Create a nonce
        createNonce: function () {
            var n = new Uint8Array(24);
            for (var i = 0; i < 24; i++) { n[i] = 0; }
            return n;
        },

        // Increment a nonce
        increment: function (N) {
            var l = N.length;
            while (l-- > 1) {
                /* .jshint probably suspects this is unsafe because we lack types
                   but as long as this is only used on nonces, it should be safe  */
                if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line

                // you don't need to worry about this running out.
                // you'd need a REAAAALLY big file
                if (l === 0) { throw new Error('E_NONCE_TOO_LARGE'); }

                N[l] = 0;
            }
        },

        decodePrefix: function (A) {
            return (A[0] << 8) | A[1];
        },
        joinChunks: function (chunks) {
            return new Blob(chunks);
        },

        // Convert a Uint8Array into Array.
        slice: function (u8) {
            return Array.prototype.slice.call(u8);
        },

        // Gets the key from the key string.
        getKeyFromStr: function (str) {
            return window.nacl.util.decodeBase64(str);
        }
    };

    // Decrypts a Uint8Array with the given key.
    var decrypt = function (u8, strKey, done, progressCb) {
        var Nacl = window.nacl;

        var progress = function (offset) {
            progressCb((offset / u8.length) * 100);
        };

        var key = Decrypt.getKeyFromStr(strKey);
        var nonce = Decrypt.createNonce();
        var i = 0;
        var prefix = u8.subarray(0, 2);
        var metadataLength = Decrypt.decodePrefix(prefix);

        var res = { metadata: undefined };

        // Get metadata
        var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
        var metaChunk = Nacl.secretbox.open(metaBox, nonce, key);

        Decrypt.increment(nonce);

        try { res.metadata = JSON.parse(Nacl.util.encodeUTF8(metaChunk)); }
        catch (e) { return void done('E_METADATA_DECRYPTION'); }

        if (!res.metadata) { return void done('NO_METADATA'); }

        var takeChunk = function (cb) {
            setTimeout(function () {
                var start = i * cypherChunkLength + 2 + metadataLength;
                var end = start + cypherChunkLength;
                i++;

                // Get the chunk
                var box = new Uint8Array(u8.subarray(start, end));

                // Decrypt the chunk
                var plaintext = Nacl.secretbox.open(box, nonce, key);
                Decrypt.increment(nonce);

                if (!plaintext) { return void cb('DECRYPTION_FAILURE'); }

                progress(Math.min(end, u8.length));

                cb(void 0, plaintext);
            });
        };

        var chunks = [];

        // decrypt file contents
        var again = function () {
            takeChunk(function (e, plaintext) {
                if (e) { return setTimeout(function () { done(e); }); }

                if (plaintext) {
                    if ((i * cypherChunkLength + 2 + metadataLength) < u8.length) { // not done
                        chunks.push(plaintext);
                        return again();
                    }

                    chunks.push(plaintext);
                    res.content = Decrypt.joinChunks(chunks);
                    return void done(void 0, res);
                }
                done('UNEXPECTED_ENDING');
            });
        };
        again();
    };

    // Get type
    var getType = function (mediaObject, metadata, cfg) {
        var mime = metadata.type;
        var s = metadata.type.split('/');
        var type = s[0];
        var extension = s[1];

        mediaObject.name = metadata.name;
        if (mime && cfg.allowed.indexOf(mime) !== -1) {
            mediaObject.type = type;
            mediaObject.extension = extension;
            mediaObject.mime = mime;
            return type;
        } else if (cfg.allowed.indexOf('download') !== -1) {
            mediaObject.type = type;
            mediaObject.extension = extension;
            mediaObject.mime = mime;
            return 'download';
        } else {
            return;
        }
    };

    // Copy attributes
    var copyAttributes = function (origin, dest) {
        Object.keys(origin.attributes).forEach(function (i) {
            if (!/^data-attr/.test(origin.attributes[i].name)) { return; }
            var name = origin.attributes[i].name.slice(10);
            var value = origin.attributes[i].value;
            dest.setAttribute(name, value);
        });
    };

    // Process
    var process = function (mediaObject, decrypted, cfg, cb) {
        var metadata = decrypted.metadata;
        var blob = decrypted.content;

        var mediaType = getType(mediaObject, metadata, cfg);
        if (isplainTextFile(metadata)) {
            mediaType = "text";
        }

        if (mediaType === 'application') {
            mediaType = mediaObject.extension;
        }

        if (!mediaType || !cfg.Plugins[mediaType]) {
            return void cb('NO_PLUGIN_FOUND');
        }

        // Get blob URL
        var url = decrypted.url;
        if (!url && window.URL) {
            url = decrypted.url = window.URL.createObjectURL(new Blob([blob], {
                type: metadata.type
            }));
        }

        cfg.Plugins[mediaType](metadata, url, blob, cfg, function (err, el) {
            if (err || !el) { return void cb(err || 'ERR_MEDIATAG_DISPLAY'); }
            copyAttributes(mediaObject.tag, el);
            mediaObject.tag.innerHTML = '';
            mediaObject.tag.appendChild(el);
            cb();
        });
    };

    var addMissingConfig = function (base, target) {
        Object.keys(target).forEach(function (k) {
            if (!target[k]) { return; }
            // Target is an object, fix it recursively
            if (typeof target[k] === "object" && !Array.isArray(target[k])) {
                // Sub-object
                if (base[k] && (typeof base[k] !== "object" || Array.isArray(base[k]))) { return; }
                else if (base[k]) { addMissingConfig(base[k], target[k]); }
                else {
                    base[k] = {};
                    addMissingConfig(base[k], target[k]);
                }
            }
            // Target is array or immutable, copy the value if it's missing
            if (!base[k]) {
                base[k] = Array.isArray(target[k]) ? JSON.parse(JSON.stringify(target[k]))
                                                   : target[k];
            }
        });
    };

    // Initialize a media-tag
    var init = function (el, cfg) {
        cfg = cfg || {};

        addMissingConfig(cfg, config);

        // Handle jQuery elements
        if (typeof(el) === "object" && el.jQuery) { el = el[0]; }

        // Abort smoothly if the element is not a media-tag
        if (!el || el.nodeName !== "MEDIA-TAG") {
            console.error("Not a media-tag!");
            return {
                on: function () { return this; }
            };
        }

        var handlers = cfg.handlers || {
            'progress': [],
            'complete': [],
            'error': []
        };

        var mediaObject = el._mediaObject = {
            handlers: handlers,
            tag: el
        };

        var emit = function (ev, data) {
            // Check if the event name is valid
            if (Object.keys(handlers).indexOf(ev) === -1) {
                return void console.error("Invalid mediatag event");
            }

            // Call the handlers
            handlers[ev].forEach(function (h) {
                // Make sure a bad handler won't break the media-tag script
                try {
                    h(data);
                } catch (err) {
                    console.error(err);
                }
            });
        };

        mediaObject.on = function (ev, handler) {
            // Check if the event name is valid
            if (Object.keys(handlers).indexOf(ev) === -1) {
                console.error("Invalid mediatag event");
                return mediaObject;
            }
            // Check if the handler is valid
            if (typeof (handler) !== "function") {
                console.error("Handler is not a function!");
                return mediaObject;
            }
            // Add the handler
            handlers[ev].push(handler);
            return mediaObject;
        };

        var src = el.getAttribute('src');
        var strKey = el.getAttribute('data-crypto-key');
        if (/^cryptpad:/.test(strKey)) {
            strKey = strKey.slice(9);
        }
        var uid = [src, strKey].join('');

        // End media-tag rendering: display the tag and emit the event
        var end = function (decrypted) {
            process(mediaObject, decrypted, cfg, function (err) {
                if (err) { return void emit('error', err); }
                mediaObject._blob = decrypted;
                emit('complete', decrypted);
            });
        };

        // If we have the blob in our cache, don't download & decrypt it again, just display
        if (cache[uid]) {
            end(cache[uid]);
            return mediaObject;
        }

        // Download the encrypted blob
        download(src, function (err, u8Encrypted) {
            if (err) {
                return void emit('error', err);
            }
            // Decrypt the blob
            decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) {
                if (errDecryption) {
                    return void emit('error', errDecryption);
                }
                // Cache and display the decrypted blob
                cache[uid] = u8Decrypted;
                end(u8Decrypted);
            }, function (progress) {
                emit('progress', {
                    progress: progress
                });
            });
        });

        return mediaObject;
    };

    // Add the cache as a property of MediaTag
    cache = init.__Cryptpad_Cache = {};

    init.setDefaultConfig = function (key, value) {
        config[key] = value;
    };

    return init;
}));
<commit_msg>Placeholder while loading mediatag and broken images for 404<commit_after>(function(name, definition) {
    if (typeof module !== 'undefined') { module.exports = definition(); }
    else if (typeof define === 'function' && typeof define.amd === 'object') { define(definition); }
    else  { this[name] = definition(); }
}('MediaTag', function() {
    var cache;
    var cypherChunkLength = 131088;

    // Save a blob on the file system
    var saveFile = function (blob, url, fileName) {
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveOrOpenBlob(blob, fileName);
        } else {
            // We want to be able to download the file with a name, so we need an "a" tag with
            // a download attribute
            var a = document.createElement("a");
            a.href = url;
            a.download = fileName;
            // It's not in the DOM, so we can't use a.click();
            var event = new MouseEvent("click");
            a.dispatchEvent(event);
        }
    };

    var fixHTML = function (str) {
        if (!str) { return ''; }
        return str.replace(/[<>&"']/g, function (x) {
            return ({ "<": "<", ">": "&gt", "&": "&", '"': """, "'": "'" })[x];
        });
    };

    var isplainTextFile = function (metadata) {
        // does its type begins with "text/"
        if (metadata.type.indexOf("text/") === 0) { return true; }
        // no type and no file extension -> let's guess it's plain text
        var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(metadata.name) || [];
        if (!metadata.type && !parsedName[2]) { return true; }
        // other exceptions
        if (metadata.type === 'application/x-javascript') { return true; }
        if (metadata.type === 'application/xml') { return true; }
        return false;
    };

    // Default config, can be overriden per media-tag call
    var config = {
        allowed: [
            'text/plain',
            'image/png',
            'image/jpeg',
            'image/jpg',
            'image/gif',
            'audio/mp3',
            'audio/ogg',
            'audio/wav',
            'audio/webm',
            'video/mp4',
            'video/ogg',
            'video/webm',
            'application/pdf',
            //'application/dash+xml', // FIXME?
            'download'
        ],
        pdf: {},
        download: {
            text: "Download"
        },
        Plugins: {
            /**
             * @param {object}   metadataObject {name,  metadatatype, owners} containing metadata of the file
             * @param {strint}   url     Url of the blob object
             * @param {Blob}     content Blob object containing the data of the file
             * @param {object}   cfg     Object {Plugins, allowed, download, pdf} containing infos about plugins
             * @param {function} cb      Callback function: (err, pluginElement) => {}
             */
            text: function (metadata, url, content, cfg, cb) {
                var plainText = document.createElement('div');
                plainText.className = "plain-text-reader";
                plainText.setAttribute('style', 'white-space: pre-wrap;');
                var reader = new FileReader();
                reader.addEventListener('loadend', function (e) {
                    plainText.innerText = e.srcElement.result;
                    cb(void 0, plainText);
                });
                reader.readAsText(content);
            },
            image: function (metadata, url, content, cfg, cb) {
                var img = document.createElement('img');
                img.setAttribute('src', url);
                img.blob = content;
                cb(void 0, img);
            },
            video: function (metadata, url, content, cfg, cb) {
                var video = document.createElement('video');
                video.setAttribute('src', url);
                video.setAttribute('controls', true);
                cb(void 0, video);
            },
            audio: function (metadata, url, content, cfg, cb) {
                var audio = document.createElement('audio');
                audio.setAttribute('src', url);
                audio.setAttribute('controls', true);
                cb(void 0, audio);
            },
            pdf: function (metadata, url, content, cfg, cb) {
                var iframe = document.createElement('iframe');
                if (cfg.pdf.viewer) { // PDFJS
                    var viewerUrl = cfg.pdf.viewer + '?file=' + url;
                    iframe.src = viewerUrl + '#' + window.encodeURIComponent(metadata.name);
                    return void cb (void 0, iframe);
                }
                iframe.src = url + '#' + window.encodeURIComponent(metadata.name);
                return void cb (void 0, iframe);
            },
            download: function (metadata, url, content, cfg, cb) {
                var btn = document.createElement('button');
                btn.setAttribute('class', 'btn btn-success');
                btn.innerHTML = cfg.download.text + '
' +
                                (metadata.name ? '' + fixHTML(metadata.name) + '' : '');
                btn.addEventListener('click', function () {
                    saveFile(content, url, metadata.name);
                });
                cb(void 0, btn);
            }
        }
    };

    // Download a blob from href
    var download = function (src, _cb) {
        var cb = function (e, res) {
            _cb(e, res);
            cb = function () {};
        };

        var xhr = new XMLHttpRequest();
        xhr.open('GET', src, true);
        xhr.responseType = 'arraybuffer';

        xhr.onerror = function () { return void cb("XHR_ERROR"); };
        xhr.onload = function () {
            // Error?
            if (/^4/.test('' + this.status)) { return void cb("XHR_ERROR " + this.status); }

            var arrayBuffer = xhr.response;
            if (arrayBuffer) { cb(null, new Uint8Array(arrayBuffer)); }
        };

        xhr.send(null);
    };

    // Decryption tools
    var Decrypt = {
        // Create a nonce
        createNonce: function () {
            var n = new Uint8Array(24);
            for (var i = 0; i < 24; i++) { n[i] = 0; }
            return n;
        },

        // Increment a nonce
        increment: function (N) {
            var l = N.length;
            while (l-- > 1) {
                /* .jshint probably suspects this is unsafe because we lack types
                   but as long as this is only used on nonces, it should be safe  */
                if (N[l] !== 255) { return void N[l]++; } // jshint ignore:line

                // you don't need to worry about this running out.
                // you'd need a REAAAALLY big file
                if (l === 0) { throw new Error('E_NONCE_TOO_LARGE'); }

                N[l] = 0;
            }
        },

        decodePrefix: function (A) {
            return (A[0] << 8) | A[1];
        },
        joinChunks: function (chunks) {
            return new Blob(chunks);
        },

        // Convert a Uint8Array into Array.
        slice: function (u8) {
            return Array.prototype.slice.call(u8);
        },

        // Gets the key from the key string.
        getKeyFromStr: function (str) {
            return window.nacl.util.decodeBase64(str);
        }
    };

    // Decrypts a Uint8Array with the given key.
    var decrypt = function (u8, strKey, done, progressCb) {
        var Nacl = window.nacl;

        var progress = function (offset) {
            progressCb((offset / u8.length) * 100);
        };

        var key = Decrypt.getKeyFromStr(strKey);
        var nonce = Decrypt.createNonce();
        var i = 0;
        var prefix = u8.subarray(0, 2);
        var metadataLength = Decrypt.decodePrefix(prefix);

        var res = { metadata: undefined };

        // Get metadata
        var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
        var metaChunk = Nacl.secretbox.open(metaBox, nonce, key);

        Decrypt.increment(nonce);

        try { res.metadata = JSON.parse(Nacl.util.encodeUTF8(metaChunk)); }
        catch (e) { return void done('E_METADATA_DECRYPTION'); }

        if (!res.metadata) { return void done('NO_METADATA'); }

        var takeChunk = function (cb) {
            setTimeout(function () {
                var start = i * cypherChunkLength + 2 + metadataLength;
                var end = start + cypherChunkLength;
                i++;

                // Get the chunk
                var box = new Uint8Array(u8.subarray(start, end));

                // Decrypt the chunk
                var plaintext = Nacl.secretbox.open(box, nonce, key);
                Decrypt.increment(nonce);

                if (!plaintext) { return void cb('DECRYPTION_FAILURE'); }

                progress(Math.min(end, u8.length));

                cb(void 0, plaintext);
            });
        };

        var chunks = [];

        // decrypt file contents
        var again = function () {
            takeChunk(function (e, plaintext) {
                if (e) { return setTimeout(function () { done(e); }); }

                if (plaintext) {
                    if ((i * cypherChunkLength + 2 + metadataLength) < u8.length) { // not done
                        chunks.push(plaintext);
                        return again();
                    }

                    chunks.push(plaintext);
                    res.content = Decrypt.joinChunks(chunks);
                    return void done(void 0, res);
                }
                done('UNEXPECTED_ENDING');
            });
        };
        again();
    };

    // Get type
    var getType = function (mediaObject, metadata, cfg) {
        var mime = metadata.type;
        var s = metadata.type.split('/');
        var type = s[0];
        var extension = s[1];

        mediaObject.name = metadata.name;
        if (mime && cfg.allowed.indexOf(mime) !== -1) {
            mediaObject.type = type;
            mediaObject.extension = extension;
            mediaObject.mime = mime;
            return type;
        } else if (cfg.allowed.indexOf('download') !== -1) {
            mediaObject.type = type;
            mediaObject.extension = extension;
            mediaObject.mime = mime;
            return 'download';
        } else {
            return;
        }
    };

    // Copy attributes
    var copyAttributes = function (origin, dest) {
        Object.keys(origin.attributes).forEach(function (i) {
            if (!/^data-attr/.test(origin.attributes[i].name)) { return; }
            var name = origin.attributes[i].name.slice(10);
            var value = origin.attributes[i].value;
            dest.setAttribute(name, value);
        });
    };

    // Process
    var process = function (mediaObject, decrypted, cfg, cb) {
        var metadata = decrypted.metadata;
        var blob = decrypted.content;

        var mediaType = getType(mediaObject, metadata, cfg);
        if (isplainTextFile(metadata)) {
            mediaType = "text";
        }

        if (mediaType === 'application') {
            mediaType = mediaObject.extension;
        }

        if (!mediaType || !cfg.Plugins[mediaType]) {
            return void cb('NO_PLUGIN_FOUND');
        }

        // Get blob URL
        var url = decrypted.url;
        if (!url && window.URL) {
            url = decrypted.url = window.URL.createObjectURL(new Blob([blob], {
                type: metadata.type
            }));
        }

        cfg.Plugins[mediaType](metadata, url, blob, cfg, function (err, el) {
            if (err || !el) { return void cb(err || 'ERR_MEDIATAG_DISPLAY'); }
            copyAttributes(mediaObject.tag, el);
            mediaObject.tag.innerHTML = '';
            mediaObject.tag.appendChild(el);
            cb();
        });
    };

    var addMissingConfig = function (base, target) {
        Object.keys(target).forEach(function (k) {
            if (!target[k]) { return; }
            // Target is an object, fix it recursively
            if (typeof target[k] === "object" && !Array.isArray(target[k])) {
                // Sub-object
                if (base[k] && (typeof base[k] !== "object" || Array.isArray(base[k]))) { return; }
                else if (base[k]) { addMissingConfig(base[k], target[k]); }
                else {
                    base[k] = {};
                    addMissingConfig(base[k], target[k]);
                }
            }
            // Target is array or immutable, copy the value if it's missing
            if (!base[k]) {
                base[k] = Array.isArray(target[k]) ? JSON.parse(JSON.stringify(target[k]))
                                                   : target[k];
            }
        });
    };

    // Initialize a media-tag
    var init = function (el, cfg) {
        cfg = cfg || {};

        addMissingConfig(cfg, config);

        // Handle jQuery elements
        if (typeof(el) === "object" && el.jQuery) { el = el[0]; }

        // Abort smoothly if the element is not a media-tag
        if (!el || el.nodeName !== "MEDIA-TAG") {
            console.error("Not a media-tag!");
            return {
                on: function () { return this; }
            };
        }

        var handlers = cfg.handlers || {
            'progress': [],
            'complete': [],
            'error': []
        };

        var mediaObject = el._mediaObject = {
            handlers: handlers,
            tag: el
        };

        var emit = function (ev, data) {
            // Check if the event name is valid
            if (Object.keys(handlers).indexOf(ev) === -1) {
                return void console.error("Invalid mediatag event");
            }

            // Call the handlers
            handlers[ev].forEach(function (h) {
                // Make sure a bad handler won't break the media-tag script
                try {
                    h(data);
                } catch (err) {
                    console.error(err);
                }
            });
        };

        mediaObject.on = function (ev, handler) {
            // Check if the event name is valid
            if (Object.keys(handlers).indexOf(ev) === -1) {
                console.error("Invalid mediatag event");
                return mediaObject;
            }
            // Check if the handler is valid
            if (typeof (handler) !== "function") {
                console.error("Handler is not a function!");
                return mediaObject;
            }
            // Add the handler
            handlers[ev].push(handler);
            return mediaObject;
        };

        var src = el.getAttribute('src');
        var strKey = el.getAttribute('data-crypto-key');
        if (/^cryptpad:/.test(strKey)) {
            strKey = strKey.slice(9);
        }
        var uid = [src, strKey].join('');

        // End media-tag rendering: display the tag and emit the event
        var end = function (decrypted) {
            process(mediaObject, decrypted, cfg, function (err) {
                if (err) { return void emit('error', err); }
                mediaObject._blob = decrypted;
                emit('complete', decrypted);
            });
        };

        // If we have the blob in our cache, don't download & decrypt it again, just display
        if (cache[uid]) {
            end(cache[uid]);
            return mediaObject;
        }

        mediaObject.tag.innerHTML = '';

        // Download the encrypted blob
        download(src, function (err, u8Encrypted) {
            if (err) {
                if (err === "XHR_ERROR 404") {
                    mediaObject.tag.innerHTML = '';
                }
                return void emit('error', err);
            }
            // Decrypt the blob
            decrypt(u8Encrypted, strKey, function (errDecryption, u8Decrypted) {
                if (errDecryption) {
                    return void emit('error', errDecryption);
                }
                // Cache and display the decrypted blob
                cache[uid] = u8Decrypted;
                end(u8Decrypted);
            }, function (progress) {
                emit('progress', {
                    progress: progress
                });
            });
        });

        return mediaObject;
    };

    // Add the cache as a property of MediaTag
    cache = init.__Cryptpad_Cache = {};

    init.setDefaultConfig = function (key, value) {
        config[key] = value;
    };

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


<commit_before>import Vue from "vue";
import VueRouter from "vue-router";
import { getRemoteData } from "../webapp/lib/utils";
import { EventBus } from "../webapp/lib/mixins/event_bus";
import { checkAndReportAccessibility } from "lib/vue/accesibility";

if (Vue.config.devtools) {
  Vue.use(checkAndReportAccessibility)
}
Vue.use(VueRouter);
Vue.config.productionTip = false;

export class CostsController {
  constructor(options) {
    this.charts = {};

    const selector = "gobierto-visualizations-costs-app";

    // Mount Vue applications
    const entryPoint = document.getElementById(selector);

    if (entryPoint) {
      const htmlRouterBlock = `<router-view></router-view>`;

      entryPoint.innerHTML = htmlRouterBlock;

      const Home = () =>
        import("../webapp/containers/costs/Home.vue");
      const TableFirstLevel = () =>
        import("../webapp/containers/costs/table/TableFirstLevel.vue");
      const TableSecondLevel = () =>
        import("../webapp/containers/costs/table/TableSecondLevel.vue");
      const TableItem = () =>
        import("../webapp/containers/costs/table/TableItem.vue");

      Promise.resolve(getRemoteData(options.costsEndpoint)).then(rawData => {
        this.setGlobalVariables(rawData);

        const router = new VueRouter({
          mode: "history",
          routes: [
            {
              path: "/visualizaciones/costes/",
              name: "Home",
              component: Home,
              children: [
                {
                  path: "/visualizaciones/costes/:year?",
                  component: TableFirstLevel,
                  name: "TableFirstLevel"
                },
                {
                  path: "/visualizaciones/costes/:year?/:id?",
                  component: TableSecondLevel,
                  name: "TableSecondLevel"
                },
                {
                  path: "/visualizaciones/costes/:year?/:id?/:item?",
                  component: TableItem,
                  name: "TableItem"
                }
              ]
            }
          ],
          scrollBehavior(to) {
            let element;
            //Get a different position scroll
            if (to.name !== "TableFirstLevel") {
              element = document.getElementById(
                "gobierto-visualizations-title-detail"
              );
              element.scrollIntoView({ behavior: "smooth" });
            }
          }
        });

        const baseTitle = document.title;
        router.afterEach(to => {
          // Wait 2 ticks
          Vue.nextTick(() =>
            Vue.nextTick(() => {
              let title = baseTitle;
              if (to.name === "TableItem") {
                const { description: itemTitle } = to.params;

                if (itemTitle) {
                  title = `${itemTitle} ${baseTitle}`;
                }
              } else if (to.name === "TableSecondLevel") {
                const { description: itemTitle } = to.params;

                if (itemTitle) {
                  title = `${itemTitle} ${baseTitle}`;
                }
              }
              document.title = title;
            })
          );
        });

        this.vueApp = new Vue({
          router,
          data: Object.assign(options, this.data)
        }).$mount(entryPoint);

        EventBus.$on("mounted", () => {
          // Hide the external loader once the vueApp has been mounted in the DOM
          const loadingElement = document.querySelector(".js-loading");
          if (loadingElement) {
            loadingElement.classList.add("hidden");
          }
        });
      });
    }
  }

  setGlobalVariables(rawData) {
    //Convert strings with some format to Numbers without format
    const toNumber = (value) => value ? +value : +(parseFloat(value)) || 0
    //Array with all the strings that we've to convert to Number
    const amountStrings = [
      "costdirecte",
      "costindirecte",
      "costtotal",
      "costperhabit",
      "costpersonal",
      "costrestadir",
      "ingressos",
      "taxapreupub",
      "subvencions",
      "ingressos_cost",
      "costdirectepers",
      "costdirebens",
      "costdirservext",
      "costdirtransf",
      "costdirequip",
      "costdirfin"
    ];

    const population = ["128291", "129661", "129120"];

    let yearsCosts = [...new Set(rawData.map(item => item.any_))];
    const setPopulation = (value, item) => value[yearsCosts.findIndex(year => year === item["any_"])]

    const data = rawData.map((item) => {
      for (let amounts of amountStrings) {
        item[amounts] = toNumber(item[amounts]);
      }
      return {
        population: setPopulation(population, item),
        ...item
      }
    })

    let groupDataByYears = [];

    //Create an array of objects with all years
    for (let index = 0; index < yearsCosts.length; index++) {
      const data = rawData.reduce((acc, item) => {
        if (item.any_ === yearsCosts[index]) {
          acc.push({ ...item, population: population[index] });
        }
        return acc;
      }, []);
      const dataGroup = groupDataByYear(data, yearsCosts[index]);
      groupDataByYears.push(dataGroup);
    }

    function groupDataByYear(data, year) {
      //Filter groupData by "sumadatos": https://github.com/PopulateTools/issues/issues/1097
      let dataFilterSum = data.filter(({ sumadatos }) => sumadatos === "");
      let groupData = [
        ...dataFilterSum
          .reduce((r, o) => {
            const key = o.agrupacio;

            const item =
              r.get(key) ||
              Object.assign({}, o, {
                costdirecte: 0,
                costindirecte: 0,
                costtotal: 0,
                ingressos: 0,
                total: 0,
                totalPerHabitant: 0,
                coverage: 0
              });

            item.costdirecte += o.costdirecte;
            item.costindirecte += o.costindirecte;
            item.costtotal += o.costtotal;
            item.ingressos += o.ingressos;
            //New item with the sum of values of each agrupacio
            item.total += (o.total || 0) + 1;
            item.coverage = (item.ingressos * 100) / item.costtotal;
            item.totalPerHabitant = item.costtotal / o.population;

            return r.set(key, item);
          }, new Map())
          .values()
      ];
      console.log("groupData", groupData);
      return (groupData = groupData.filter(
        ({ agrupacio, any_ }) => agrupacio !== "" && any_ === year
      ));
    }

    groupDataByYears = groupDataByYears
      .flat()
      .sort(({ costtotal: a }, { costtotal: b }) => (a > b ? -1 : 1));

    this.data = {
      costData: data,
      groupData: groupDataByYears,
      yearsCosts: yearsCosts
    };
  }
}
<commit_msg>remove console<commit_after>import Vue from "vue";
import VueRouter from "vue-router";
import { getRemoteData } from "../webapp/lib/utils";
import { EventBus } from "../webapp/lib/mixins/event_bus";
import { checkAndReportAccessibility } from "lib/vue/accesibility";

if (Vue.config.devtools) {
  Vue.use(checkAndReportAccessibility)
}
Vue.use(VueRouter);
Vue.config.productionTip = false;

export class CostsController {
  constructor(options) {
    this.charts = {};

    const selector = "gobierto-visualizations-costs-app";

    // Mount Vue applications
    const entryPoint = document.getElementById(selector);

    if (entryPoint) {
      const htmlRouterBlock = `<router-view></router-view>`;

      entryPoint.innerHTML = htmlRouterBlock;

      const Home = () =>
        import("../webapp/containers/costs/Home.vue");
      const TableFirstLevel = () =>
        import("../webapp/containers/costs/table/TableFirstLevel.vue");
      const TableSecondLevel = () =>
        import("../webapp/containers/costs/table/TableSecondLevel.vue");
      const TableItem = () =>
        import("../webapp/containers/costs/table/TableItem.vue");

      Promise.resolve(getRemoteData(options.costsEndpoint)).then(rawData => {
        this.setGlobalVariables(rawData);

        const router = new VueRouter({
          mode: "history",
          routes: [
            {
              path: "/visualizaciones/costes/",
              name: "Home",
              component: Home,
              children: [
                {
                  path: "/visualizaciones/costes/:year?",
                  component: TableFirstLevel,
                  name: "TableFirstLevel"
                },
                {
                  path: "/visualizaciones/costes/:year?/:id?",
                  component: TableSecondLevel,
                  name: "TableSecondLevel"
                },
                {
                  path: "/visualizaciones/costes/:year?/:id?/:item?",
                  component: TableItem,
                  name: "TableItem"
                }
              ]
            }
          ],
          scrollBehavior(to) {
            let element;
            //Get a different position scroll
            if (to.name !== "TableFirstLevel") {
              element = document.getElementById(
                "gobierto-visualizations-title-detail"
              );
              element.scrollIntoView({ behavior: "smooth" });
            }
          }
        });

        const baseTitle = document.title;
        router.afterEach(to => {
          // Wait 2 ticks
          Vue.nextTick(() =>
            Vue.nextTick(() => {
              let title = baseTitle;
              if (to.name === "TableItem") {
                const { description: itemTitle } = to.params;

                if (itemTitle) {
                  title = `${itemTitle} ${baseTitle}`;
                }
              } else if (to.name === "TableSecondLevel") {
                const { description: itemTitle } = to.params;

                if (itemTitle) {
                  title = `${itemTitle} ${baseTitle}`;
                }
              }
              document.title = title;
            })
          );
        });

        this.vueApp = new Vue({
          router,
          data: Object.assign(options, this.data)
        }).$mount(entryPoint);

        EventBus.$on("mounted", () => {
          // Hide the external loader once the vueApp has been mounted in the DOM
          const loadingElement = document.querySelector(".js-loading");
          if (loadingElement) {
            loadingElement.classList.add("hidden");
          }
        });
      });
    }
  }

  setGlobalVariables(rawData) {
    //Convert strings with some format to Numbers without format
    const toNumber = (value) => value ? +value : +(parseFloat(value)) || 0
    //Array with all the strings that we've to convert to Number
    const amountStrings = [
      "costdirecte",
      "costindirecte",
      "costtotal",
      "costperhabit",
      "costpersonal",
      "costrestadir",
      "ingressos",
      "taxapreupub",
      "subvencions",
      "ingressos_cost",
      "costdirectepers",
      "costdirebens",
      "costdirservext",
      "costdirtransf",
      "costdirequip",
      "costdirfin"
    ];

    const population = ["128291", "129661", "129120"];

    let yearsCosts = [...new Set(rawData.map(item => item.any_))];
    const setPopulation = (value, item) => value[yearsCosts.findIndex(year => year === item["any_"])]

    const data = rawData.map((item) => {
      for (let amounts of amountStrings) {
        item[amounts] = toNumber(item[amounts]);
      }
      return {
        population: setPopulation(population, item),
        ...item
      }
    })

    let groupDataByYears = [];

    //Create an array of objects with all years
    for (let index = 0; index < yearsCosts.length; index++) {
      const data = rawData.reduce((acc, item) => {
        if (item.any_ === yearsCosts[index]) {
          acc.push({ ...item, population: population[index] });
        }
        return acc;
      }, []);
      const dataGroup = groupDataByYear(data, yearsCosts[index]);
      groupDataByYears.push(dataGroup);
    }

    function groupDataByYear(data, year) {
      //Filter groupData by "sumadatos": https://github.com/PopulateTools/issues/issues/1097
      let dataFilterSum = data.filter(({ sumadatos }) => sumadatos === "");
      let groupData = [
        ...dataFilterSum
          .reduce((r, o) => {
            const key = o.agrupacio;

            const item =
              r.get(key) ||
              Object.assign({}, o, {
                costdirecte: 0,
                costindirecte: 0,
                costtotal: 0,
                ingressos: 0,
                total: 0,
                totalPerHabitant: 0,
                coverage: 0
              });

            item.costdirecte += o.costdirecte;
            item.costindirecte += o.costindirecte;
            item.costtotal += o.costtotal;
            item.ingressos += o.ingressos;
            //New item with the sum of values of each agrupacio
            item.total += (o.total || 0) + 1;
            item.coverage = (item.ingressos * 100) / item.costtotal;
            item.totalPerHabitant = item.costtotal / o.population;

            return r.set(key, item);
          }, new Map())
          .values()
      ];
      return (groupData = groupData.filter(
        ({ agrupacio, any_ }) => agrupacio !== "" && any_ === year
      ));
    }

    groupDataByYears = groupDataByYears
      .flat()
      .sort(({ costtotal: a }, { costtotal: b }) => (a > b ? -1 : 1));

    this.data = {
      costData: data,
      groupData: groupDataByYears,
      yearsCosts: yearsCosts
    };
  }
}
<|endoftext|>


<commit_before>ScudCloud = {
	unloaded: true,
	// App functions
	hasPreference: function(name){
		return false;
	},
	getPreference: function(name){
		return false;
	},
	setPreference: function(name, value){
		return false;
	},
	canShowHtmlNotifications: function(){
		// Ubuntu cannot display HTML notifications
		return false;
	},
	// TSSSB.call
	call: function(name, args){
		ScudCloud.log(name, args);
		switch(name){
			case "reload":
				return ScudCloud.reload();
			case "didStartLoading":
				return ScudCloud.didStartLoading();
			case "didFinishLoading":
				return ScudCloud.didFinishLoading();
			case "setConnectionStatus":
				return ScudCloud.setConnectionStatus(args);
			case "notify":
				return ScudCloud.notify(args);
			case "setBadgeCount":
				return ScudCloud.setBadgeCount(args);
			case "displayTeam":
				return ScudCloud.displayTeam(args);
			case "signInTeam":
				return ScudCloud.signInTeam();
		}
		return false;
	},
	// TSSSB.call implementations
	reload: function(){
		window.location.reload();
	},
	didStartLoading: function(){
	},
	didFinishLoading: function(){
		TS.ui.banner.close();
		ScudCloud.populate();
		ScudCloud.unloaded = false;
	},
	setConnectionStatus: function(status){
		// "online", "connecting", "offline"
		switch(status){
			case "online": desktop.enableMenus(true); break;
			default: desktop.enableMenus(false);
		}
	},
	notify: function(args){
		desktop.sendMessage(args.title, args.content);
	},
	setBadgeCount: function(args){
		desktop.count(args.all_unread_highlights_cnt, args.all_unread_cnt);
	},
	signInTeam: function(){
		desktop.addTeam();
	},
	displayTeam: function(id){
	},
	// ScudCloud internal functions
	log: function(name, args){
		if("object"== typeof(args)) args = JSON.stringify(args);
		console.log("ScudCloud."+name+", args: "+args);
	},
	populate: function(){
		setTimeout(function(){
			desktop.populate(JSON.stringify({'channels': ScudCloud.listChannels(), 'teams': ScudCloud.listTeams(), 'icon': TS.model.team.icon.image_44}));
		}, 500);
	},
	createSnippet: function(){
		return TS.ui.snippet_dialog.start();
	},
	listChannels: function(){
		var channels = TS.channels.getUnarchivedChannelsForUser();
		channels.push(TS.channels.getChannelById(TS.model.active_channel_id));
		return channels;
	},
	listTeams: function(){
		var list = TS.getAllTeams();
		// Fix for current team displaying no icon
		list[0].team_icon = {"image_44":TS.model.team.icon.image_44};
		return list;
	},
	quicklist: function(){
		desktop.quicklist(ScudCloud.listChannels());
	},
	join: function(c){
		return TS.channels.join(c);
	},
	setClipboard: function(data){
		TS.client.ui.file_pasted_sig.dispatch(data, TS.model.shift_key_pressed);
	},
	sendTickle: function(){
		return TS.ms.sendTickle();
	},
	preferences: function(){
		return TS.ui.prefs_dialog.start();
	},
	addTeam: function(){
		document.location = TS.boot_data.signin_url;
	},
	getCurrentTeam: function(){
		var list = TS.getAllTeams();
		if(list!=null) for(var i=0;list.length;i++){
			if(list[i].team_url==TS.boot_data.team_url){
				return list[i].id;
			}
		}
		return "";
	},
	logout: function(){
		document.location = TS.boot_data.logout_url;
	},
	help: function(){
		return TS.help_dialog.start();
	}
};
document.onpaste = function(e){desktop.pasted(false);};
// Forcing new posts to get opened in system browser (Fixes #225)
$('body').delegate('a[href="/files/create/space"]', 'click', function(){desktop.open(TS.boot_data.team_url+'files/create/space');});
// Fixing profile display CSS (Fixes #396)
$('body').delegate('#client-ui', 'DOMNodeInserted',
    function(){
        var obj = $('.member_preview_link.member_image.thumb_512');
        if(obj.length > 0){
            var style = obj.attr('style');
						if(-1==style.indexOf('-webkit-linear-gradient')){
							obj.attr('style', style.replace('linear-gradient', '-webkit-linear-gradient'));
						}
        }
    }
);
// Forcing call button handling
$('body').delegate('#channel_calls_button', 'click', function(){desktop.open(TS.boot_data.team_url+'call/'+TS.model.active_cid);});
window.winssb = TSSSB = ScudCloud;
// Sometimes didFinishLoading is not loaded
if(ScudCloud.unloaded){
	ScudCloud.didFinishLoading();
}
<commit_msg>Explaining change (#454)<commit_after>ScudCloud = {
	unloaded: true,
	// App functions
	hasPreference: function(name){
		return false;
	},
	getPreference: function(name){
		return false;
	},
	setPreference: function(name, value){
		return false;
	},
	canShowHtmlNotifications: function(){
		// Ubuntu cannot display HTML notifications
		return false;
	},
	// TSSSB.call
	call: function(name, args){
		ScudCloud.log(name, args);
		switch(name){
			case "reload":
				return ScudCloud.reload();
			case "didStartLoading":
				return ScudCloud.didStartLoading();
			case "didFinishLoading":
				return ScudCloud.didFinishLoading();
			case "setConnectionStatus":
				return ScudCloud.setConnectionStatus(args);
			case "notify":
				return ScudCloud.notify(args);
			case "setBadgeCount":
				return ScudCloud.setBadgeCount(args);
			case "displayTeam":
				return ScudCloud.displayTeam(args);
			case "signInTeam":
				return ScudCloud.signInTeam();
		}
		return false;
	},
	// TSSSB.call implementations
	reload: function(){
		window.location.reload();
	},
	didStartLoading: function(){
	},
	didFinishLoading: function(){
		TS.ui.banner.close();
		ScudCloud.populate();
		ScudCloud.unloaded = false;
	},
	setConnectionStatus: function(status){
		// "online", "connecting", "offline"
		switch(status){
			case "online": desktop.enableMenus(true); break;
			default: desktop.enableMenus(false);
		}
	},
	notify: function(args){
		desktop.sendMessage(args.title, args.content);
	},
	setBadgeCount: function(args){
		desktop.count(args.all_unread_highlights_cnt, args.all_unread_cnt);
	},
	signInTeam: function(){
		desktop.addTeam();
	},
	displayTeam: function(id){
	},
	// ScudCloud internal functions
	log: function(name, args){
		if("object"== typeof(args)) args = JSON.stringify(args);
		console.log("ScudCloud."+name+", args: "+args);
	},
	populate: function(){
		// Wait until image_44 get ready (Fixes #454)
		setTimeout(function(){
			desktop.populate(JSON.stringify({'channels': ScudCloud.listChannels(), 'teams': ScudCloud.listTeams(), 'icon': TS.model.team.icon.image_44}));
		}, 500);
	},
	createSnippet: function(){
		return TS.ui.snippet_dialog.start();
	},
	listChannels: function(){
		var channels = TS.channels.getUnarchivedChannelsForUser();
		channels.push(TS.channels.getChannelById(TS.model.active_channel_id));
		return channels;
	},
	listTeams: function(){
		var list = TS.getAllTeams();
		// Fix for current team displaying no icon
		list[0].team_icon = {"image_44":TS.model.team.icon.image_44};
		return list;
	},
	quicklist: function(){
		desktop.quicklist(ScudCloud.listChannels());
	},
	join: function(c){
		return TS.channels.join(c);
	},
	setClipboard: function(data){
		TS.client.ui.file_pasted_sig.dispatch(data, TS.model.shift_key_pressed);
	},
	sendTickle: function(){
		return TS.ms.sendTickle();
	},
	preferences: function(){
		return TS.ui.prefs_dialog.start();
	},
	addTeam: function(){
		document.location = TS.boot_data.signin_url;
	},
	getCurrentTeam: function(){
		var list = TS.getAllTeams();
		if(list!=null) for(var i=0;list.length;i++){
			if(list[i].team_url==TS.boot_data.team_url){
				return list[i].id;
			}
		}
		return "";
	},
	logout: function(){
		document.location = TS.boot_data.logout_url;
	},
	help: function(){
		return TS.help_dialog.start();
	}
};
document.onpaste = function(e){desktop.pasted(false);};
// Forcing new posts to get opened in system browser (Fixes #225)
$('body').delegate('a[href="/files/create/space"]', 'click', function(){desktop.open(TS.boot_data.team_url+'files/create/space');});
// Fixing profile display CSS (Fixes #396)
$('body').delegate('#client-ui', 'DOMNodeInserted',
    function(){
        var obj = $('.member_preview_link.member_image.thumb_512');
        if(obj.length > 0){
            var style = obj.attr('style');
						if(-1==style.indexOf('-webkit-linear-gradient')){
							obj.attr('style', style.replace('linear-gradient', '-webkit-linear-gradient'));
						}
        }
    }
);
// Forcing call button handling
$('body').delegate('#channel_calls_button', 'click', function(){desktop.open(TS.boot_data.team_url+'call/'+TS.model.active_cid);});
window.winssb = TSSSB = ScudCloud;
// Sometimes didFinishLoading is not loaded
if(ScudCloud.unloaded){
	ScudCloud.didFinishLoading();
}
<|endoftext|>