<commit_before>(function() {

var clip = new ZeroClipboard.Client();
clip.setHandCursor(true);

function create_show_package_link(pkg) {
    return ''+pkg+'';
}

function setup_copy_to_clipboard(obj, urls) {
    var items = [];
    var len = urls.length;
    for (var i=0; i<len; i++) {
        items.push($(urls[i]).text());
    }
    obj.data('urls', items.join("\n"));

    obj.mouseover(function() {
        clip.setText($(this).data('urls'));
        if (clip.div) {
            clip.receiveEvent('mouseout', null);
            clip.reposition(this);
        }
        else {
            clip.glue(this);
        }
        clip.receiveEvent('mouseover', null);
        var self = this;
        clip.addEventListener('onComplete', function() {
            $(self).text('Copied!');
        });
    });
}

function show_loader(obj, text) {
    if (text == undefined) {
        text = 'loading..';
    }

    obj.html('

 '+text+'

');
    obj.show();
}

var ph = {
    install: {
        packages: [],

        _add: function(pkgs) {
            var items = pkgs.split(" ");
            var len = items.length;
            for (var i=0; i<len; i++) {
                var item = items[i];
                if ((item.length > 0) && (this.packages.indexOf(item) == -1)) {
                    this.packages.push(item);
                }
            }
        },

        _remove: function(pkg) {
            var pos = this.packages.indexOf(pkg);
            if (pos >= 0) {
                this.packages.splice(pos, 1);
            }
        },

        _clear: function() {
            this.packages = [];
        },

        update_list: function() {
            var obj = $('#install-package-list ul');
            var html = '';
            var len = this.packages.length;
            for (var i=0; i<len; i++) {
                var pkg = this.packages[i];
                html += '
'+create_show_package_link(pkg);
                html += 'x
 ';
            }
            obj.html(html);
            if (len > 0) {
                $('#install-package-button').show();
                $('#install-package-list').show();
            }
            else {
                $('#install-package-button').hide();
                $('#install-package-list').hide();
            }
        },

        add_form: function() {
            var pkgs = $('#install-package-add')[0].value;
            this.add(pkgs);
            $('#install-package-add').select();
        },

        add: function(pkgs) {
            this._add(pkgs);
            this.update_list();
        },

        remove: function(pkg) {
            this._remove(pkg);
            this.update_list();
        },

        clear: function() {
            this._clear();
            this.update_list();
            $('#install-package-add').select();
        },

        _create_package_list: function(items, title) {
            if (items == undefined) { return ''; }
            var len = items.length;
            if (len == 0) { return ''; }

            var html = '';
            html += '

'+title+'
';

            var list = [];
            for (var i=0; i<len; i++) {
                list.push(create_show_package_link(items[i]));
            }

            html += '
' + list.join(', ') + '
';

            html += '
';
            return html;
        },

        show: function(data, target) {
            if (target == undefined) {
                target = '#install-result';
            }

            var obj = $(target);
            var type = data.type;
            var data = data.data;
            var html = '';

            obj.html('

');
            obj = obj.find('.install-result');

            var pkgs = data.packages.join(' ');

            if (type == 'install') {
                html += '
Installation result for '+pkgs+':
';
            }
            else if (type == "upgrade") {
                html += '
Result of upgrade operation:
';
            }
            else if (type == "dist-upgrade") {
                html += '
Result of dist-upgrade operation:
';
            }

            if (data.errors.length > 0) {
                html += '

Unable to install. Error messages:

';
                var len = data.errors.length;
                for (var i=0; i<len; i++) {
                    html += '
'+data.errors[i]+'
';
                }
                html += '

';
                obj.html(html);
            }
            else {
                html += '

';
                obj.html(html);

                var head = obj.find('.head');
                var side = obj.find('.side');
                var main = obj.find('.main');

                // Already the newest version
                if (data.newest.length > 0) {
                    var items = [];
                    var len = data.newest.length;
                    for (var i=0; i<len; i++) {
                        items.push(create_show_package_link(data.newest[i]));
                    }
                    html = '
Already the newest version: ' + items.join(', ') + '
';
                    head.html(html);
                }

                // URL list
                if (data.urls.length > 0) {
                    html = '';
                    html += '
URLs
';
                    html += '
 — copy to clipboard
';
                    html += '
';

                    var len = data.urls.length;
                    for (var i=0; i<len; i++) {
                        var item = data.urls[i];
                        var url = item[0];
                        var deb = item[1];
                        var size = item[2];
                        var hash = item[3];
                        var hash_format = item[4];
                        html += '
'+url+'
';
                    }

                    html += '
';
                    main.html(html);

                    setup_copy_to_clipboard(main.find('p.copy span'), main.find('ul.urls li'));
                }

                // install

                html = '';
                html += this._create_package_list(data.install, 'To Be Installed');
                html += this._create_package_list(data.upgrade, 'To Be Upgraded');
                html += this._create_package_list(data.recommended, 'Recommended');
                html += this._create_package_list(data.suggested, 'Suggested');

                side.html(html);
            }

            obj.append('
');
            obj.show();
        },

        start: function() {
            var packages = this.packages.join(" ");
            var self = this;
            show_loader($('#install-result'));
            $.ajax({
                type: 'POST',
                url: 'install/?format=json',
                dataType: 'json',
                data: {packages: packages},
                complete: function(xhr, stat) {
                    console.log('complete.', 'stat:', stat);
                },
                success: function(data, stat) {
                    self.show(data);
                }
            });
        }
    },

    show: function(pkg) {
        var dialog = $('#dialog-package-show');
        dialog.find('h3').text(pkg);
        dialog.find('div').text('');
        dialog.find('p').text('');
        dialog.find('pre').html('');
        dialog.find('table').html('');
        dialog.dialog('open');
        show_loader(dialog);
        $.ajax({
            url: 'show/'+pkg+'/?format=json',
            method: 'GET',
            dataType: 'json',
            success: function(data, stat) {
                if (data.data.package == null) {
                    var html = '';
                    html += '

No package information for '+pkg+'

';
                    dialog.html(html);
                }
                else {
                    pkg = data.data.package;

                    var html = '';
                    html += '
'+data.data.sdesc+'
';
                    html += '
'+data.data.desc+'
';
                    html += '
';

                    var table = dialog.find('table');
                    var info = data.data.data;
                    var len = info.length;
                    for (var i=0; i<len; i++) {
                        var key = info[i][0];
                        var value = htmlentities(info[i][1]);
                        if (key != 'Description') {
                            html += '
'+key+'	'+value+'
';
                        }
                    }
                    html += '
';

                    dialog.html(html);
                }

                dialog.dialog('option', 'position', ['center', 25]);
                dialog.dialog('option', 'title', pkg);
            },

            error: function(xhr, stat, err) {
                if (xhr.status == 404) {
                    var html = '';
                    html += '

Package not found: '+pkg+'

';
                    dialog.html(html);
                }
            },
        });
    },

    search: {

        show: function(query, data) {
            var items = data.data.items;
            var len = items.length;
            var obj = $('#search-result');

            if (len == 0) {
                var html = '';
                html += '
No package found. Please try another keyword.
';
                obj.html(html);
            }
            else {
                var html = '';
                html += '
Search result for '+query+':
';
                html += '

';
                obj.html(html);
                var table = obj.find('table');
                table.append('
	Package	Description
');
                for (var i=0; i<len; i++) {
                    var pkg = items[i][0];
                    var desc = items[i][1];
                    var pkglink = create_show_package_link(pkg);
                    var addlink = '';
                    if (ph.install.packages.indexOf(pkg) == -1) {
                        addlink = 'add';
                    }
                    else {
                        addlink = 'added';
                    }
                    table.append('
'+(i+1)+'	'+pkglink+'	'+desc+'	'+addlink+'
');
                }
            }
            obj.show();
        },

        add: function(src, pkg) {
            ph.install.add(pkg);
            $(src).parent().html('added');
        },

        start: function(f) {
            var query = f.packages.value;
            var self = this;
            show_loader($('#search-result'));
            $.ajax({
                type: 'POST',
                url: 'search/?format=json',
                dataType: 'json',
                data: {packages: query},
                complete: function(xhr, stat) {
                    console.log('complete');
                    console.log('stat:', stat);
                },
                success: function(data, stat) {
                    self.show(query, data);
                }
            });
        }
    },

    dist_upgrade: {
        start: function() {
            ph.upgrade._start('dist-upgrade');
        }
    },

    upgrade: {
        start: function() {
            this._start('upgrade');
        },

        _start: function(path) {
            show_loader($('#upgrade-result'));
            $.ajax({
                type: 'POST',
                url: path + '/?format=json',
                dataType: 'json',
                complete: function(xhr, stat) {
                    console.log('complete.', 'stat:', stat);
                },
                success: function(data, stat) {
                    ph.install.show(data, '#upgrade-result');
                }
            });
        }
    },

    update: {
        start: function(sender) {
            var btn = $(sender).find('input[type="submit"]');
            btn.attr('disabled', 'disabled');
            btn.after(' updating..');

            $.ajax({
                type: 'POST',
                url: 'update/',
                dataType: 'text',
                complete: function(xhr, stat) {
                },
                success: function(data, stat) {
                    btn.attr('disabled', '');
                    console.log(window.location.href);
                    window.location.reload();
                },
            });
        }
    },

    init: function() {
        this._init_dialogs();
        this._init_tabs();
    },

    _init_dialogs: function() {
        $('.dialog').dialog({autoOpen: false, modal: true});
        $('#dialog-status-upload').dialog('option', 'width', 500);
        $('#dialog-sources').dialog('option', 'width', 500);
        $('#dialog-package-show').dialog('option', 'width', 700);
        $('#dialog-package-show').dialog('option', 'maxHeight', 500);
    },

    _init_tabs: function() {
        $('.tabs').tabs();
    }
}

this.profile = ph;
})();

function show_dialog(s) {
    $(s).dialog('open');
}
function htmlentities(txt) {
    txt = txt.replace(/</g, '<');
    txt = txt.replace(/>/g, '>');
    return txt;
}
<commit_msg>Mark and unmark package<commit_after>(function() {

var clip = new ZeroClipboard.Client();
clip.setHandCursor(true);

function create_show_package_link(pkg) {
    return ''+pkg+'';
}

function setup_copy_to_clipboard(obj, urls) {
    var items = [];
    var len = urls.length;
    for (var i=0; i<len; i++) {
        items.push($(urls[i]).text());
    }
    obj.data('urls', items.join("\n"));

    obj.mouseover(function() {
        clip.setText($(this).data('urls'));
        if (clip.div) {
            clip.receiveEvent('mouseout', null);
            clip.reposition(this);
        }
        else {
            clip.glue(this);
        }
        clip.receiveEvent('mouseover', null);
        var self = this;
        clip.addEventListener('onComplete', function() {
            $(self).text('Copied!');
        });
    });
}

function show_loader(obj, text) {
    if (text == undefined) {
        text = 'loading..';
    }

    obj.html('

 '+text+'

');
    obj.show();
}

var ph = {
    install: {
        packages: [],

        _add: function(pkgs) {
            var items = pkgs.split(" ");
            var len = items.length;
            for (var i=0; i<len; i++) {
                var item = items[i];
                if ((item.length > 0) && (this.packages.indexOf(item) == -1)) {
                    this.packages.push(item);
                }
            }
        },

        _remove: function(pkg) {
            var pos = this.packages.indexOf(pkg);
            if (pos >= 0) {
                this.packages.splice(pos, 1);
            }
        },

        _clear: function() {
            this.packages = [];
        },

        update_list: function() {
            var obj = $('#install-package-list ul');
            var html = '';
            var len = this.packages.length;
            for (var i=0; i<len; i++) {
                var pkg = this.packages[i];
                html += '
'+create_show_package_link(pkg);
                html += 'x
 ';
            }
            obj.html(html);
            if (len > 0) {
                $('#install-package-button').show();
                $('#install-package-list').show();
            }
            else {
                $('#install-package-button').hide();
                $('#install-package-list').hide();
            }
        },

        add_form: function() {
            var pkgs = $('#install-package-add')[0].value;
            this.add(pkgs);
            $('#install-package-add').select();
        },

        add: function(pkgs) {
            this._add(pkgs);
            this.update_list();
        },

        remove: function(pkg) {
            this._remove(pkg);
            this.update_list();
        },

        clear: function() {
            this._clear();
            this.update_list();
            $('#install-package-add').select();
        },

        _create_package_list: function(items, title) {
            if (items == undefined) { return ''; }
            var len = items.length;
            if (len == 0) { return ''; }

            var html = '';
            html += '

'+title+'
';

            var list = [];
            for (var i=0; i<len; i++) {
                list.push(create_show_package_link(items[i]));
            }

            html += '
' + list.join(', ') + '
';

            html += '
';
            return html;
        },

        show: function(data, target) {
            if (target == undefined) {
                target = '#install-result';
            }

            var obj = $(target);
            var type = data.type;
            var data = data.data;
            var html = '';

            obj.html('

');
            obj = obj.find('.install-result');

            var pkgs = data.packages.join(' ');

            if (type == 'install') {
                html += '
Installation result for '+pkgs+':
';
            }
            else if (type == "upgrade") {
                html += '
Result of upgrade operation:
';
            }
            else if (type == "dist-upgrade") {
                html += '
Result of dist-upgrade operation:
';
            }

            if (data.errors.length > 0) {
                html += '

Unable to install. Error messages:

';
                var len = data.errors.length;
                for (var i=0; i<len; i++) {
                    html += '
'+data.errors[i]+'
';
                }
                html += '

';
                obj.html(html);
            }
            else {
                html += '

';
                obj.html(html);

                var head = obj.find('.head');
                var side = obj.find('.side');
                var main = obj.find('.main');

                // Already the newest version
                if (data.newest.length > 0) {
                    var items = [];
                    var len = data.newest.length;
                    for (var i=0; i<len; i++) {
                        items.push(create_show_package_link(data.newest[i]));
                    }
                    html = '
Already the newest version: ' + items.join(', ') + '
';
                    head.html(html);
                }

                // URL list
                if (data.urls.length > 0) {
                    html = '';
                    html += '
URLs
';
                    html += '
 — copy to clipboard
';
                    html += '
';

                    var len = data.urls.length;
                    for (var i=0; i<len; i++) {
                        var item = data.urls[i];
                        var url = item[0];
                        var deb = item[1];
                        var size = item[2];
                        var hash = item[3];
                        var hash_format = item[4];
                        html += '
'+url+'
';
                    }

                    html += '
';
                    main.html(html);

                    setup_copy_to_clipboard(main.find('p.copy span'), main.find('ul.urls li'));
                }

                // install

                html = '';
                html += this._create_package_list(data.install, 'To Be Installed');
                html += this._create_package_list(data.upgrade, 'To Be Upgraded');
                html += this._create_package_list(data.recommended, 'Recommended');
                html += this._create_package_list(data.suggested, 'Suggested');

                side.html(html);
            }

            obj.append('
');
            obj.show();
        },

        start: function() {
            var packages = this.packages.join(" ");
            var self = this;
            show_loader($('#install-result'));
            $.ajax({
                type: 'POST',
                url: 'install/?format=json',
                dataType: 'json',
                data: {packages: packages},
                complete: function(xhr, stat) {
                    console.log('complete.', 'stat:', stat);
                },
                success: function(data, stat) {
                    self.show(data);
                }
            });
        }
    },

    show: function(pkg) {
        var dialog = $('#dialog-package-show');
        dialog.find('h3').text(pkg);
        dialog.find('div').text('');
        dialog.find('p').text('');
        dialog.find('pre').html('');
        dialog.find('table').html('');
        dialog.dialog('open');
        show_loader(dialog);
        $.ajax({
            url: 'show/'+pkg+'/?format=json',
            method: 'GET',
            dataType: 'json',
            success: function(data, stat) {
                if (data.data.package == null) {
                    var html = '';
                    html += '

No package information for '+pkg+'

';
                    dialog.html(html);
                }
                else {
                    pkg = data.data.package;

                    var html = '';
                    html += '
'+data.data.sdesc+'
';
                    html += '
'+data.data.desc+'
';
                    html += '
';

                    var table = dialog.find('table');
                    var info = data.data.data;
                    var len = info.length;
                    for (var i=0; i<len; i++) {
                        var key = info[i][0];
                        var value = htmlentities(info[i][1]);
                        if (key != 'Description') {
                            html += '
'+key+'	'+value+'
';
                        }
                    }
                    html += '
';

                    dialog.html(html);
                }

                dialog.dialog('option', 'position', ['center', 25]);
                dialog.dialog('option', 'title', pkg);
            },

            error: function(xhr, stat, err) {
                if (xhr.status == 404) {
                    var html = '';
                    html += '

Package not found: '+pkg+'

';
                    dialog.html(html);
                }
            },
        });
    },

    search: {

        show: function(query, data) {
            var items = data.data.items;
            var len = items.length;
            var obj = $('#search-result');

            if (len == 0) {
                var html = '';
                html += '
No package found. Please try another keyword.
';
                obj.html(html);
            }
            else {
                var html = '';
                html += '
Search result for '+query+':
';
                html += '

';
                obj.html(html);
                var table = obj.find('table');
                table.append('
	Package	Description
');
                for (var i=0; i<len; i++) {
                    var pkg = items[i][0];
                    var desc = items[i][1];
                    var pkglink = create_show_package_link(pkg);
                    var addlink = '';
                    if (ph.install.packages.indexOf(pkg) == -1) {
                        addlink = 'mark';
                    }
                    else {
                        addlink = 'unmark';
                    }
                    table.append('
'+(i+1)+'	'+pkglink+'	'+desc+'	'+addlink+'
');
                }
            }
            obj.show();
        },

        add: function(src, pkg) {
            ph.install.add(pkg);
            $(src).parent().html('unmark');
        },

        remove: function(src, pkg) {
            ph.install.remove(pkg);
            $(src).parent().html('mark');
        },

        start: function(f) {
            var query = f.packages.value;
            var self = this;
            show_loader($('#search-result'));
            $.ajax({
                type: 'POST',
                url: 'search/?format=json',
                dataType: 'json',
                data: {packages: query},
                complete: function(xhr, stat) {
                    console.log('complete');
                    console.log('stat:', stat);
                },
                success: function(data, stat) {
                    self.show(query, data);
                }
            });
        }
    },

    dist_upgrade: {
        start: function() {
            ph.upgrade._start('dist-upgrade');
        }
    },

    upgrade: {
        start: function() {
            this._start('upgrade');
        },

        _start: function(path) {
            show_loader($('#upgrade-result'));
            $.ajax({
                type: 'POST',
                url: path + '/?format=json',
                dataType: 'json',
                complete: function(xhr, stat) {
                    console.log('complete.', 'stat:', stat);
                },
                success: function(data, stat) {
                    ph.install.show(data, '#upgrade-result');
                }
            });
        }
    },

    update: {
        start: function(sender) {
            var btn = $(sender).find('input[type="submit"]');
            btn.attr('disabled', 'disabled');
            btn.after(' updating..');

            $.ajax({
                type: 'POST',
                url: 'update/',
                dataType: 'text',
                complete: function(xhr, stat) {
                },
                success: function(data, stat) {
                    btn.attr('disabled', '');
                    console.log(window.location.href);
                    window.location.reload();
                },
            });
        }
    },

    init: function() {
        this._init_dialogs();
        this._init_tabs();
    },

    _init_dialogs: function() {
        $('.dialog').dialog({autoOpen: false, modal: true});
        $('#dialog-status-upload').dialog('option', 'width', 500);
        $('#dialog-sources').dialog('option', 'width', 500);
        $('#dialog-package-show').dialog('option', 'width', 700);
        $('#dialog-package-show').dialog('option', 'maxHeight', 500);
    },

    _init_tabs: function() {
        $('.tabs').tabs();
    }
}

this.profile = ph;
})();

function show_dialog(s) {
    $(s).dialog('open');
}
function htmlentities(txt) {
    txt = txt.replace(/</g, '<');
    txt = txt.replace(/>/g, '>');
    return txt;
}
<|endoftext|>


<commit_before>function createContextMenu() {
  chrome.contextMenus.create({ id: 'bracketless', title: 'getAll()' });
}
// function updateContextMenu(text) {
//   chrome.contextMenus.update('bracketless', { title: text });
// }

const tabState = {
  get(tabId) {
    return new Promise((resolve) => {
      chrome.storage.local.get(tabId.toString(), (state) => {
        resolve(state[tabId]);
      });
    });
  },
  set(tabId, value) {
    return new Promise((resolve) => {
      chrome.storage.local.set({ [tabId]: value }, () => resolve());
    });
  },
  remove(tabId) {
    return new Promise((resolve) => {
      chrome.storage.local.remove(tabId.toString(), () => resolve());
    });
  },
  removeWithCheck(tabId) {
    this.get(tabId).then((state) => {
      if (state !== undefined) this.remove(tabId);
    });
  },
  clearAll() {
    chrome.storage.local.clear();
  },
  getAll() {
    return new Promise((resolve) => {
      chrome.storage.local.get(null, (all) => {
        console.log(all);
        const activeIdArr = Object.keys(all).map(key => Number(key));
        resolve(activeIdArr);
      });
    });
  },
};

function load(tabId) {
  return new Promise((resolve) => {
    chrome.tabs.executeScript(tabId, { file: 'src/bracketless.js' }, () => {
      chrome.browserAction.setIcon({ tabId, path: 'icons/play.png' });
      chrome.browserAction.setTitle({ tabId, title: 'Collapse brackets' });
      // updateContextMenu('Collapse brackets');
      chrome.tabs.insertCSS(tabId, { file: 'css/action.css' });
      chrome.tabs.executeScript(tabId, { file: 'src/action.js' }, () => {
        tabState.set(tabId, false)
          .then(() => resolve('load resolved'));
      });
    });
  });
}

function activate(tabId, active) {
  return new Promise((resolve) => {
    const action = active === true ?
      { message: 'Pause collapsing', reverseIcon: 'pause' } :
      { message: 'Collapse brackets', reverseIcon: 'play' };
    chrome.browserAction.setIcon({ tabId, path: `icons/${action.reverseIcon}.png` });
    chrome.browserAction.setTitle({ tabId, title: action.message });
    // updateContextMenu(state.message);
    chrome.tabs.sendMessage(tabId, active, () => {
      tabState.set(tabId, active)
        .then(() => { resolve(`action resolved. enabled: ${active}`); });
    });
  });
}

function listenerAction(tabId) {
  tabState.get(tabId)
    .then((state) => {
      if (state === undefined) {
        return load(tabId);
      } else if (state === false) {
        return activate(tabId, true);
      } else if (state === true) {
        return activate(tabId, false);
      }
      return Promise.reject(new Error('listenerAction if else'));
    })
    .catch((reason) => {
      throw Error(`Bracketless. listenerAction. Reason: ${reason}`);
    });
}

function autoAction(tabId) {
  // call fns directly to bypass listenerAction condition checks
  chrome.storage.sync.get(null, (options) => {
    if (options.autoPlay) {
      load(tabId).then(() => activate(tabId, true));
    } else if (options.autoLoad) {
      load(tabId);
    }
  });
}

function addOnUpdated() {
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    // Works only with auto options. Chrome browser utility tab check (requires tab permission)
    if (/(chrome)(?:[/:-])/.test(tab.url)) return;

    if (changeInfo.status === 'complete' && tab.active) {
      // no permission, either new tab or tab refresh
      console.log(`tab.url is ${tab.url}`);
      if (tab.url === undefined) {
        tabState.removeWithCheck(tabId);
        // else will also catch when permission granted via activeTab, FIX IT
      } else {
        if (/#/.test(tab.url)) {
          console.log('this test is inefficient as it will prevent autoLoad from working');
        }
        autoAction(tabId);
      }
    }
  });
}

function getOpenTabIdList() {
  return new Promise((resolve) => {
    chrome.tabs.query({ currentWindow: true }, (tabList) => {
      resolve(tabList.map(tab => tab.id));
    });
  });
}
function garbageCollector() {
  return Promise.all([tabState.getAll(), getOpenTabIdList()])
    .then((tabIdArr) => {
      const activeList = tabIdArr[0];
      const openList = tabIdArr[1];

      if (activeList.length > 4) {
        activeList.forEach((id) => {
          console.log(openList.includes(id));
          if (openList.includes(id) === false) {
            tabState.remove(id);
          }
        });
      }
    });
}

function checkPermission(permissionObj) {
  return new Promise((resolve, reject) => {
    chrome.permissions.contains(permissionObj, (result) => {
      if (result) resolve(result);
      else reject(result);
    });
  });
}

function addOnRemoved() {
  const tabsPerm = {
    permissions: ['tabs'],
    origins: ['http://*/', 'https://*/'],
  };

  chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
    // tab is focused (matters only for activeTab) and browser window gets closed
    if (removeInfo.isWindowClosing) {
      tabState.clearAll();
      return;
    }

    checkPermission(tabsPerm)
      .then(() => { // autoOptions enabled, tabs permission obtained
        tabState.removeWithCheck(tabId);
      }, () => { // autoOptions disabled, clean up via activeTab
        garbageCollector(removeInfo);
      })
      .catch((reason) => {
        throw Error(`Bracketless. addOnRemoved. Reason: ${reason}`);
      });
  });
}

function syncDefault() {
  chrome.storage.sync.set({
    lowLimit: 13,
    upLimit: 255,
    autoLoad: false,
    autoPlay: false,
  });
}
function checkOptsUse(cb) {
  chrome.storage.sync.getBytesInUse(null, (bytes) => { if (bytes === 0) cb(); });
}

function onInstalled() {
  chrome.runtime.onInstalled.addListener(() => {
    checkOptsUse(syncDefault); // if not in use, sync default
    createContextMenu();

    // listeners
    chrome.browserAction.onClicked.addListener(tab => listenerAction(tab.id));
    chrome.contextMenus.onClicked.addListener((_, tab) => tabState.getAll());
    addOnUpdated();
    addOnRemoved();
  });
}

onInstalled();
<commit_msg>Remove .removeWithCheck(), replace with .remove()<commit_after>function createContextMenu() {
  chrome.contextMenus.create({ id: 'bracketless', title: 'getAll()' });
}
// function updateContextMenu(text) {
//   chrome.contextMenus.update('bracketless', { title: text });
// }

const tabState = {
  get(tabId) {
    return new Promise((resolve) => {
      chrome.storage.local.get(tabId.toString(), (state) => {
        resolve(state[tabId]);
      });
    });
  },
  set(tabId, value) {
    return new Promise((resolve) => {
      chrome.storage.local.set({ [tabId]: value }, () => resolve());
    });
  },
  remove(tabId) {
    return new Promise((resolve) => {
      chrome.storage.local.remove(tabId.toString(), () => resolve());
    });
  },
  clearAll() {
    chrome.storage.local.clear();
  },
  getAll() {
    return new Promise((resolve) => {
      chrome.storage.local.get(null, (all) => {
        console.log(all);
        const activeIdArr = Object.keys(all).map(key => Number(key));
        resolve(activeIdArr);
      });
    });
  },
};

function load(tabId) {
  return new Promise((resolve) => {
    chrome.tabs.executeScript(tabId, { file: 'src/bracketless.js' }, () => {
      chrome.browserAction.setIcon({ tabId, path: 'icons/play.png' });
      chrome.browserAction.setTitle({ tabId, title: 'Collapse brackets' });
      // updateContextMenu('Collapse brackets');
      chrome.tabs.insertCSS(tabId, { file: 'css/action.css' });
      chrome.tabs.executeScript(tabId, { file: 'src/action.js' }, () => {
        tabState.set(tabId, false)
          .then(() => resolve('load resolved'));
      });
    });
  });
}

function activate(tabId, active) {
  return new Promise((resolve) => {
    const action = active === true ?
      { message: 'Pause collapsing', reverseIcon: 'pause' } :
      { message: 'Collapse brackets', reverseIcon: 'play' };
    chrome.browserAction.setIcon({ tabId, path: `icons/${action.reverseIcon}.png` });
    chrome.browserAction.setTitle({ tabId, title: action.message });
    // updateContextMenu(state.message);
    chrome.tabs.sendMessage(tabId, active, () => {
      tabState.set(tabId, active)
        .then(() => { resolve(`action resolved. enabled: ${active}`); });
    });
  });
}

function listenerAction(tabId) {
  tabState.get(tabId)
    .then((state) => {
      if (state === undefined) {
        return load(tabId);
      } else if (state === false) {
        return activate(tabId, true);
      } else if (state === true) {
        return activate(tabId, false);
      }
      return Promise.reject(new Error('listenerAction if else'));
    })
    .catch((reason) => {
      throw Error(`Bracketless. listenerAction. Reason: ${reason}`);
    });
}

function autoAction(tabId) {
  // call fns directly to bypass listenerAction condition checks
  chrome.storage.sync.get(null, (options) => {
    if (options.autoPlay) {
      load(tabId).then(() => activate(tabId, true));
    } else if (options.autoLoad) {
      load(tabId);
    }
  });
}

function addOnUpdated() {
  chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    // Works only with auto options. Chrome browser utility tab check (requires tab permission)
    if (/(chrome)(?:[/:-])/.test(tab.url)) return;

    if (changeInfo.status === 'complete' && tab.active) {
      // no permission, either new tab or tab refresh
      console.log(`tab.url is ${tab.url}`);
      if (tab.url === undefined) {
        tabState.remove(tabId);
        // else will also catch when permission granted via activeTab, FIX IT
      } else {
        if (/#/.test(tab.url)) {
          console.log('this test is inefficient as it will prevent autoLoad from working');
        }
        autoAction(tabId);
      }
    }
  });
}

function getOpenTabIdList() {
  return new Promise((resolve) => {
    chrome.tabs.query({ currentWindow: true }, (tabList) => {
      resolve(tabList.map(tab => tab.id));
    });
  });
}
function garbageCollector() {
  return Promise.all([tabState.getAll(), getOpenTabIdList()])
    .then((tabIdArr) => {
      const activeList = tabIdArr[0];
      const openList = tabIdArr[1];

      if (activeList.length > 4) {
        activeList.forEach((id) => {
          console.log(openList.includes(id));
          if (openList.includes(id) === false) {
            tabState.remove(id);
          }
        });
      }
    });
}

function checkPermission(permissionObj) {
  return new Promise((resolve, reject) => {
    chrome.permissions.contains(permissionObj, (result) => {
      if (result) resolve(result);
      else reject(result);
    });
  });
}

function addOnRemoved() {
  const tabsPerm = {
    permissions: ['tabs'],
    origins: ['http://*/', 'https://*/'],
  };

  chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {
    // tab is focused (matters only for activeTab) and browser window gets closed
    if (removeInfo.isWindowClosing) {
      tabState.clearAll();
      return;
    }

    checkPermission(tabsPerm)
      .then(() => { // autoOptions enabled, tabs permission obtained
        tabState.remove(tabId);
      }, () => { // autoOptions disabled, clean up via activeTab
        garbageCollector(removeInfo);
      })
      .catch((reason) => {
        throw Error(`Bracketless. addOnRemoved. Reason: ${reason}`);
      });
  });
}

function syncDefault() {
  chrome.storage.sync.set({
    lowLimit: 13,
    upLimit: 255,
    autoLoad: false,
    autoPlay: false,
  });
}
function checkOptsUse(cb) {
  chrome.storage.sync.getBytesInUse(null, (bytes) => { if (bytes === 0) cb(); });
}

function onInstalled() {
  chrome.runtime.onInstalled.addListener(() => {
    checkOptsUse(syncDefault); // if not in use, sync default
    createContextMenu();

    // listeners
    chrome.browserAction.onClicked.addListener(tab => listenerAction(tab.id));
    chrome.contextMenus.onClicked.addListener((_, tab) => tabState.getAll());
    addOnUpdated();
    addOnRemoved();
  });
}

onInstalled();
<|endoftext|>


<commit_before>// Copyright, 2013-2014, by Tomas Korcak. <korczis@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

(function () {
    'use strict';

    var deferred = require('deferred'),
        fs = require('fs'),
        logger = require('../logger'),
        mongodb = require('mongodb'),
        mongoose = require('mongoose'),
        path = require('path');

    /**
     * Mongo wrapper
     * @type {Mongo}
     */
    var exports = module.exports = function Mongo(config) {
        this.config = config;
        this.collections = {};
        this.models = {};
    };

    /**
     * Config to be used
     * @type {object}
     */
    exports.prototype.config = null;

    /**
     * Active MongoDB database
     * @type {object}
     */
    exports.prototype.db = null;

    /**
     * Preloaded collections
     * @type {array}
     */
    exports.prototype.collections = null;

    /**
     * Loaded models
     * @type {null}
     */
    exports.prototype.models = null;

    /**
     * Connects to mongo
     * @returns {*} Promise
     */
    exports.prototype.connect = function () {
        var d = deferred();

        var MongoClient = require('mongodb').MongoClient;

        var self = this;

        MongoClient.connect(this.config.mongo.uri, function (err, db) {
            if (err) {
                throw new Error("Cannot connect to DB '" + self.config.mongo.uri + "'");
            }

            if (self.config.verbose) {
                logger.log("Connected to DB '" + self.config.mongo.uri + "'");
            }

            self.db = db;
            d.resolve(self.db);
        });

        return d.promise();
    };

    /**
     * Loads collection by name and caches it (do not use for BIG collections!)
     * @param collectionName {string} Name of the collection to be loaded
     * @returns {*} Promise
     */
    exports.prototype.loadCollection = function (collectionName) {
        var d = deferred();

        if (this.config.verbose) {
            logger.log("Loading collection '" + collectionName + "'");
        }

        var collection = this.db.collection(collectionName);

        var self = this;
        collection.find().toArray(function (err, data) {
            if (err) {
                d.reject(new Error(err));
                return;
            }

            self.collections[collectionName] = data;

            d.resolve(data);
        });

        return d.promise();
    };

    /**
     * Returns loaded collection by name as promise
     * @param collectionName
     * @returns {*}
     */
    exports.prototype.getCollection = function (collectionName) {
        if (this.config.verbose) {
            logger.log("Loading collection '" + collectionName + "'");
        }

        var collection = this.db.collection(collectionName);

        return deferred(collection);
    };

    exports.prototype.initializeModels = function () {
        var d = deferred();

        var self = this;
        var modelsDir = path.join(__dirname, "models");
        fs.readdir(modelsDir, function (err, files) {
            var res = {};

            files.forEach(function (file) {
                var parts = file.split(".");
                if(parts.length == 2 && parts[1].toLowerCase() == "js") {
                    var fullPath = modelsDir + '/' + file;

                    var relPath = path.relative(__dirname, fullPath);
                    logger.log("Loading model file '" + relPath + "'");

                    var modelName = parts[0];

                    var Model = require(fullPath);
                    var model = new Model(self);
                    res[modelName] = model;
                }
            });

            console.log(res);

            self.models = res;
            d.resolve(self);
        });

        return d.promise();
    };

    /**
     * Initializes Mongo wrapper
     * @returns {*} Promise
     */
    exports.prototype.initialize = function () {
        var d = deferred();

        var self = this;
        this.connect().then(function (res) {
            return self.initializeModels();
        }).then(function (res) {
                var opt = self.config.mongo.watcher;

                if (opt !== null && opt !== undefined && opt.toString() === "true" || opt.toString() === "1") {
                    var Watcher = require('./watcher.js');
                    self.watcher = new Watcher(self);

                    return self.watcher.initialize();
                } else {
                    d.resolve(self);
                }

            });

        return d.promise();
    };

}());<commit_msg>Better order of console.log and assignment of loaded models<commit_after>// Copyright, 2013-2014, by Tomas Korcak. <korczis@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

(function () {
    'use strict';

    var deferred = require('deferred'),
        fs = require('fs'),
        logger = require('../logger'),
        mongodb = require('mongodb'),
        mongoose = require('mongoose'),
        path = require('path');

    /**
     * Mongo wrapper
     * @type {Mongo}
     */
    var exports = module.exports = function Mongo(config) {
        this.config = config;
        this.collections = {};
        this.models = {};
    };

    /**
     * Config to be used
     * @type {object}
     */
    exports.prototype.config = null;

    /**
     * Active MongoDB database
     * @type {object}
     */
    exports.prototype.db = null;

    /**
     * Preloaded collections
     * @type {array}
     */
    exports.prototype.collections = null;

    /**
     * Loaded models
     * @type {null}
     */
    exports.prototype.models = null;

    /**
     * Connects to mongo
     * @returns {*} Promise
     */
    exports.prototype.connect = function () {
        var d = deferred();

        var MongoClient = require('mongodb').MongoClient;

        var self = this;

        MongoClient.connect(this.config.mongo.uri, function (err, db) {
            if (err) {
                throw new Error("Cannot connect to DB '" + self.config.mongo.uri + "'");
            }

            if (self.config.verbose) {
                logger.log("Connected to DB '" + self.config.mongo.uri + "'");
            }

            self.db = db;
            d.resolve(self.db);
        });

        return d.promise();
    };

    /**
     * Loads collection by name and caches it (do not use for BIG collections!)
     * @param collectionName {string} Name of the collection to be loaded
     * @returns {*} Promise
     */
    exports.prototype.loadCollection = function (collectionName) {
        var d = deferred();

        if (this.config.verbose) {
            logger.log("Loading collection '" + collectionName + "'");
        }

        var collection = this.db.collection(collectionName);

        var self = this;
        collection.find().toArray(function (err, data) {
            if (err) {
                d.reject(new Error(err));
                return;
            }

            self.collections[collectionName] = data;

            d.resolve(data);
        });

        return d.promise();
    };

    /**
     * Returns loaded collection by name as promise
     * @param collectionName
     * @returns {*}
     */
    exports.prototype.getCollection = function (collectionName) {
        if (this.config.verbose) {
            logger.log("Loading collection '" + collectionName + "'");
        }

        var collection = this.db.collection(collectionName);

        return deferred(collection);
    };

    exports.prototype.initializeModels = function () {
        var d = deferred();

        var self = this;
        var modelsDir = path.join(__dirname, "models");
        fs.readdir(modelsDir, function (err, files) {
            var res = {};

            files.forEach(function (file) {
                var parts = file.split(".");
                if(parts.length == 2 && parts[1].toLowerCase() == "js") {
                    var fullPath = modelsDir + '/' + file;

                    var relPath = path.relative(__dirname, fullPath);
                    logger.log("Loading model file '" + relPath + "'");

                    var modelName = parts[0];

                    var Model = require(fullPath);
                    var model = new Model(self);
                    res[modelName] = model;
                }
            });

            self.models = res;

            console.log(res);

            d.resolve(self);
        });

        return d.promise();
    };

    /**
     * Initializes Mongo wrapper
     * @returns {*} Promise
     */
    exports.prototype.initialize = function () {
        var d = deferred();

        var self = this;
        this.connect().then(function (res) {
            return self.initializeModels();
        }).then(function (res) {
                var opt = self.config.mongo.watcher;

                if (opt !== null && opt !== undefined && opt.toString() === "true" || opt.toString() === "1") {
                    var Watcher = require('./watcher.js');
                    self.watcher = new Watcher(self);

                    return self.watcher.initialize();
                } else {
                    d.resolve(self);
                }

            });

        return d.promise();
    };

}());<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('app')
  .controller('CreateExpenseController', ['$scope', '$rootScope', '$window', '$modalInstance', '$state', '$cacheFactory', '$http', 'Expenses', 'Auth', 'Currency', 'worldCurrencies',
  function ($scope, $rootScope, $window, $modalInstance, $state, $cacheFactory, $http, Expenses, Auth, Currency, worldCurrencies) {
    var cache = $cacheFactory.get('tripData');
    $scope.expense = {};
    var participants = cache.get('participants');
    var creatorId = $window.localStorage.getItem('userId');
    var creatorUsername = $window.localStorage.getItem('username');

    // Participants is mapped to format it in a way that the dropdown menu can understand.
    $scope.participants = participants.map(function(participant) {
      return { id: participant.id, label: participant.username };
    });
    // $scope.stakeholders is the model that the dropdown menu creates.
    $scope.stakeholders = [{id: creatorId, label: creatorUsername}];
    $scope.currencyModel = {id: 147};
    $scope.symbol = '$';

    // Configures the dropdown menu
    // http://dotansimha.github.io/angularjs-dropdown-multiselect/#/
    $scope.dropdownSettings = {
      smartButtonMaxItems: 5,
      scrollableHeight: '200px',
      scrollable: true,
      externalIdProp: '',
    };

    $scope.buttonText = {
      buttonDefaultText: 'Select Contributors'
    };

    $scope.addExpense = function () {

      // If the stakeholders model is empty add the creator to the model
      var onSuccess = function (exchangeRateobj){
        console.log("Exchange rate", exchangeRateobj.rate);
        $http.get('https://maps.googleapis.com/maps/api/geocode/json?address=' + $scope.expense.locationString)
          .then(function(result){

            if (!result.data.results[0]){
              //#TODO: Improve this error handling
              alert('Invalid location');
              return;
            }
            $scope.expense.location = result.data.results[0].geometry.location;

            if(!$scope.stakeholders.length) {
              $scope.stakeholders = [{id: creatorId, label: creatorUsername}];
            }
            // Return the data back to the server in the correct format
            $scope.stakeholders = $scope.stakeholders.map(function(stakeholder) {
              return { id: stakeholder.id, username: stakeholder.label };
            });

            //Expense String:
            $scope.expense.expenseString = $scope.expense.amount + " " +$scope.currencyData[$scope.currencyModel.id-1].label;
            //Conversion
            $scope.expense.amount = $scope.expense.amount*exchangeRateobj.rate;

            Expenses.addExpense($scope.expense, $scope.stakeholders, function (tripData) {
              console.log('expense added');
              $modalInstance.close(tripData.expenses);
            });

          });
      };

      var onError = function(err){
        //#TODO: Improve this error handling
        alert('There was a catastrophic error:' + err);
        return;
      };

      var fromCurrency = $scope.currencyData[$scope.currencyModel.id - 1].code || 'usd';
      Currency.getRate(fromCurrency, onSuccess, onError);
    };

    $scope.cancel = function () {
      $modalInstance.dismiss('cancel');
    };
    $scope.expense.date = new Date();

    /** Currency Convert Helper **/

    $scope.currDropdownSettings = {
      showCheckAll: false,
      showUncheckAll: false,
      selectionLimit: 1,
      closeOnSelect: true,
      smartButtonMaxItems: 1,
      enableSearch: true
    };

    $scope.currencyData = worldCurrencies.retrieveCurrencies();

    $scope.$watch('currencyModel.id',function(newValue,oldValue){
      $scope.symbol = $scope.currencyData[$scope.currencyModel.id - 1].symbol;
      console.log($scope.symbol);
    });
  }]);
})();<commit_msg>(feat) show symbol in expenses<commit_after>(function() {
  'use strict';

  angular.module('app')
  .controller('CreateExpenseController', ['$scope', '$rootScope', '$window', '$modalInstance', '$state', '$cacheFactory', '$http', 'Expenses', 'Auth', 'Currency', 'worldCurrencies',
  function ($scope, $rootScope, $window, $modalInstance, $state, $cacheFactory, $http, Expenses, Auth, Currency, worldCurrencies) {
    var cache = $cacheFactory.get('tripData');
    $scope.expense = {};
    var participants = cache.get('participants');
    var creatorId = $window.localStorage.getItem('userId');
    var creatorUsername = $window.localStorage.getItem('username');

    // Participants is mapped to format it in a way that the dropdown menu can understand.
    $scope.participants = participants.map(function(participant) {
      return { id: participant.id, label: participant.username };
    });
    // $scope.stakeholders is the model that the dropdown menu creates.
    $scope.stakeholders = [{id: creatorId, label: creatorUsername}];
    $scope.currencyModel = {id: 147};
    $scope.symbol = '$';

    // Configures the dropdown menu
    // http://dotansimha.github.io/angularjs-dropdown-multiselect/#/
    $scope.dropdownSettings = {
      smartButtonMaxItems: 5,
      scrollableHeight: '200px',
      scrollable: true,
      externalIdProp: '',
    };

    $scope.buttonText = {
      buttonDefaultText: 'Select Contributors'
    };

    $scope.addExpense = function () {

      // If the stakeholders model is empty add the creator to the model
      var onSuccess = function (exchangeRateobj){
        console.log("Exchange rate", exchangeRateobj.rate);
        $http.get('https://maps.googleapis.com/maps/api/geocode/json?address=' + $scope.expense.locationString)
          .then(function(result){

            if (!result.data.results[0]){
              //#TODO: Improve this error handling
              alert('Invalid location');
              return;
            }
            $scope.expense.location = result.data.results[0].geometry.location;

            if(!$scope.stakeholders.length) {
              $scope.stakeholders = [{id: creatorId, label: creatorUsername}];
            }
            // Return the data back to the server in the correct format
            $scope.stakeholders = $scope.stakeholders.map(function(stakeholder) {
              return { id: stakeholder.id, username: stakeholder.label };
            });

            //Expense String:
            $scope.expense.expenseString =$scope.symbol + $scope.expense.amount + " " +$scope.currencyData[$scope.currencyModel.id-1].label;
            //Conversion
            $scope.expense.amount = $scope.expense.amount*exchangeRateobj.rate;

            Expenses.addExpense($scope.expense, $scope.stakeholders, function (tripData) {
              console.log('expense added');
              $modalInstance.close(tripData.expenses);
            });

          });
      };

      var onError = function(err){
        //#TODO: Improve this error handling
        alert('There was a catastrophic error:' + err);
        return;
      };

      var fromCurrency = $scope.currencyData[$scope.currencyModel.id - 1].code || 'usd';
      Currency.getRate(fromCurrency, onSuccess, onError);
    };

    $scope.cancel = function () {
      $modalInstance.dismiss('cancel');
    };
    $scope.expense.date = new Date();

    /** Currency Convert Helper **/

    $scope.currDropdownSettings = {
      showCheckAll: false,
      showUncheckAll: false,
      selectionLimit: 1,
      closeOnSelect: true,
      smartButtonMaxItems: 1,
      enableSearch: true
    };

    $scope.currencyData = worldCurrencies.retrieveCurrencies();

    $scope.$watch('currencyModel.id',function(newValue,oldValue){
      $scope.symbol = $scope.currencyData[$scope.currencyModel.id - 1].symbol;
      console.log($scope.symbol);
    });
  }]);
})();<|endoftext|>


<commit_before>(function($){
    'use strict';

    var oldMax = 0;
    var oldVisible = 0;
    var oldWindowWidth = 0;

    function res () {
        var $visibleFull = $('.js-fullheight:visible');
        var heights = $visibleFull.map(function () {
            return $(this).height();
        }).get();
        var lefts = $visibleFull.map(function () {
            return $(this).offset().left;
        }).get();
        var footerpos = $('.main-footer').offset().top  -  $('header.main-header').height() + 5; // Discover where does this 5 pixels come from
        heights.push(footerpos);
        var tallerSize = Math.max.apply(null, heights);

        // cache: only run if things changed since last time
        if((oldMax === tallerSize) &&
           (oldVisible === $visibleFull.length) &&
           (oldWindowWidth === $(window).width())) {
            return;
        } else {
            oldMax = tallerSize;
            oldVisible = $visibleFull.length;
            oldWindowWidth = $(window).width();
        }

        console.log('v', oldVisible, 't', oldMax, 'f', footerpos);

        function positionBack (sel1, sel2, makeTall) {
            var $b1 = $(sel1);
            var $c1 = $(sel2 + ':visible');
            if($c1.length > 0) {
                $b1.show();
                if(makeTall)
                    $b1.height(tallerSize)
                else
                    $b1.height($c1.outerHeight())
                $b1.width($c1.outerWidth());
                $b1.offset($c1.offset());
            } else {
                $b1.hide();
            }
        }

        var makeTall = true;
        if((oldVisible == 2) && (lefts[0] === lefts[1]))
            makeTall = false;
        console.log('mt', makeTall);
        positionBack('.b1', '.c1', makeTall);
        positionBack('.b2', '.c2', makeTall);
    }

    window.timtec_res = res;

    $(function () {
        res();
        $(window).resize(res);
        // $(document).bind('DOMSubtreeModified', res);
        setTimeout(res, 500);
    });

    $(function () {
        if (window.location.pathname == '/courses') {
            $('#courses-tab').addClass('active');
        } else if (window.location.pathname == '/pages/about/') {
            $('#about-tab').addClass('active');
        } else if (window.location.pathname == '/pages/institute/') {
            $('#institute-tab').addClass('active');
        }
    });
})(window.jQuery);
<commit_msg>use js-fullheight top instead of header width<commit_after>(function($){
    'use strict';

    var oldMax = 0;
    var oldVisible = 0;
    var oldWindowWidth = 0;

    function res () {
        var $visibleFull = $('.js-fullheight:visible');
        var heights = $visibleFull.map(function () {
            return $(this).height();
        }).get();
        var lefts = $visibleFull.map(function () {
            return $(this).offset().left;
        }).get();
        var footerpos = $('.main-footer').offset().top - $visibleFull.offset().top;
        heights.push(footerpos);
        var tallerSize = Math.max.apply(null, heights);

        // cache: only run if things changed since last time
        if((oldMax === tallerSize) &&
           (oldVisible === $visibleFull.length) &&
           (oldWindowWidth === $(window).width())) {
            return;
        } else {
            oldMax = tallerSize;
            oldVisible = $visibleFull.length;
            oldWindowWidth = $(window).width();
        }

        console.log('v', oldVisible, 't', oldMax, 'f', footerpos);

        function positionBack (sel1, sel2, makeTall) {
            var $b1 = $(sel1);
            var $c1 = $(sel2 + ':visible');
            if($c1.length > 0) {
                $b1.show();
                if(makeTall)
                    $b1.height(tallerSize)
                else
                    $b1.height($c1.outerHeight())
                $b1.width($c1.outerWidth());
                $b1.offset($c1.offset());
            } else {
                $b1.hide();
            }
        }

        var makeTall = true;
        if((oldVisible == 2) && (lefts[0] === lefts[1]))
            makeTall = false;
        console.log('mt', makeTall);
        positionBack('.b1', '.c1', makeTall);
        positionBack('.b2', '.c2', makeTall);
    }

    window.timtec_res = res;

    $(function () {
        res();
        $(window).resize(res);
        // $(document).bind('DOMSubtreeModified', res);
        setTimeout(res, 500);
    });

    $(function () {
        if (window.location.pathname == '/courses') {
            $('#courses-tab').addClass('active');
        } else if (window.location.pathname == '/pages/about/') {
            $('#about-tab').addClass('active');
        } else if (window.location.pathname == '/pages/institute/') {
            $('#institute-tab').addClass('active');
        }
    });
})(window.jQuery);
<|endoftext|>


<commit_before>(function (angular) {
    "use strict";

    var app = angular.module('lesson', [
        'directive.markdowneditor',
        'directive.codemirror',
        'ngRoute',
        'ngResource',
        'youtube',
        'django',
        'forum',
        'notes'
    ]);

    window.ga = window.ga || function(){ console.log(arguments); };

    var ACTIVITY_TEMPLATE_PATH = function(the_type){
        return STATIC_URL + '/templates/activity_'+ the_type + '.html';
    };

    app.controller('MainCtrl', ['$scope', 'LessonData', 'Answer', '$location', 'youtubePlayerApi',
        function ($scope, LessonData, Answer, $location, youtubePlayerApi) {

            youtubePlayerApi.events.onStateChange = function(event){
                window.onPlayerStateChange.call($scope.currentUnit, event);
                if (event.data === YT.PlayerState.ENDED) {
                    window.$scope = $scope;
                    $scope.nextStep();
                    if(!$scope.$$phase) {
                        $scope.$apply();
                    }
                }
            };

            $scope.section = $scope.section || 'video';

            $scope.selectUnit = function(unit) {
                $scope.currentUnit = unit;
                if(unit.video) {
                    $scope.section = 'video';
                    $scope.play(unit.video.youtube_id);
                } else {
                    $scope.section = 'activity';
                }
                $scope.selectActivity(0);
            };

            $scope.nextUnit = function() {
                var index = $scope.lesson.units.indexOf($scope.currentUnit);
                index++;

                if(index < $scope.lesson.units.length) {
                    $scope.selectUnit($scope.lesson.units[index]);
                }
                // e se não tiver nextUnit, faz o que?
            };

            $scope.play = function() {
                var youtube_id = $scope.currentUnit.video.youtube_id;
                $scope.section = 'video';

                youtubePlayerApi.loadPlayer().then(function(player){
                    if(player.getVideoData() &&
                        player.getVideoData().video_id === youtube_id) return;
                    player.cueVideoById(youtube_id);
                });
            };

            $scope.selectActivity = function(index) {
                if($scope.currentUnit.activities && $scope.currentUnit.activities.length) {
                    $scope.currentActivity = $scope.currentUnit.activities[index];
                    $scope.activityTemplateUrl = ACTIVITY_TEMPLATE_PATH($scope.currentActivity.type);

                    Answer.getLastGivenAnswer($scope.currentActivity.id)
                        .then(function(answer){
                            $scope.answer = answer;
                        })
                        .catch(function(){
                            $scope.answer = new Answer();
                            if(angular.isArray($scope.currentActivity.expected)) {
                                $scope.answer.given = $scope.currentActivity.expected.map(function(){});
                            }
                        });
                } else {
                    $scope.currentActivity = null;
                    $scope.activityTemplateUrl = null;
                }
            };

            $scope.sendAnswer = function() {
                $scope.answer.activity = $scope.currentActivity.id;
                $scope.answer.saveOrUpdate().then(function(d){
                    ga('send', 'event', 'activity', 'result', '', d.correct);
                });
                ga('send', 'event', 'activity', 'submit');
            };

            $scope.nextStep = function() {
                if($scope.section === 'video') {
                    if($scope.currentUnit.activities) {
                        $scope.section = 'activity';
                    } else {
                        $scope.nextUnit();
                    }
                } else {
                    if($scope.section === 'activity' && $scope.currentActivity.comment) {
                        $scope.section = 'comment';
                    } else {
                        var index = $scope.currentUnit.activities.indexOf($scope.currentActivity);
                        if(index+1 === $scope.currentUnit.activities.length) {
                            $scope.nextUnit();
                        } else {
                            $scope.selectActivity(index + 1);
                            $scope.section = 'activity';
                        }
                    }
                }
            };

            var start;
            $scope.$watch('currentUnit', function() {
                if(!$scope.lesson) return;
                // Changing Unit means unit starting
                if (start) {
                    var end = new Date().getTime();
                    ga('send', 'event', 'unit', 'time in unit',
                       $scope.lesson.course + ' - ' + $scope.lesson.name + ' - ' + $scope.currentUnit,
                       end - start);
                }
                ga('send', 'event', 'unit', 'start', $scope.lesson.course + ' - ' + $scope.lesson.name, $scope.currentUnit);
                start = new Date().getTime();
            });

            LessonData.then(function(lesson){
                $scope.lesson = lesson;

                var index = /\/(\d+)/.extract($location.path(), 1);
                index = parseInt(index, 10) - 1 || 0;
                $scope.selectUnit(lesson.units[index]);
                $scope.play();

                $scope.$on('$locationChangeSuccess', function (event, newLoc, oldLoc){
                   index = /\/(\d+)/.extract(newLoc, 1);
                   index = parseInt(index, 10) - 1 || 0;
                   $scope.selectUnit(lesson.units[index]);
                });
            });
        }
    ]);

    app.factory('Answer',['$resource', '$q',
        function($resource, $q){
            var resourceConfig = {
                'update': {'method': 'PUT'}
            };
            var Answer = $resource('/api/answer/:id', {'id':'@id'}, resourceConfig);

            Answer.prototype.saveOrUpdate = function() {
                return this.id > 0 ? this.$update() : this.$save();
            };

            Answer.getLastGivenAnswer = function(activity_id) {
                var deferred = $q.defer();
                var extractLatest = function (list) {
                    if(list.length > 0) {
                        deferred.resolve(list.pop());
                    } else {
                        deferred.reject();
                    }
                };
                Answer.query({'activity': activity_id}, extractLatest);
                return deferred.promise;
            };

            return Answer;
        }
    ]);

    app.factory('LessonData', ['$rootScope', '$q', '$resource', '$window',
        function($rootScope, $q, $resource, $window) {
            var Lesson = $resource('/api/lessons/:lessonId/');
            var Progress = $resource('/api/student_progress?unit__lesson=:lessonId');
            var deferred = $q.defer();

            Lesson.get({'lessonId': $window.lessonId}, function (lesson) {
                $rootScope.lesson = lesson;
                deferred.resolve(lesson);
            });

            Progress.query({'lessonId': $window.lessonId}, function (progress) {
                deferred.promise.then(function (lesson) {
                    for (var i = progress.length - 1; i >= 0; i--) {
                        var p = progress[i];
                        for (var j = lesson.units.length - 1; j >= 0; j--) {
                            if (lesson.units[j].id === p.unit) {
                                lesson.units[j].progress = p;
                            }
                        }
                    }
                });
            });

            return deferred.promise;
        }
    ]);

    app.directive('radio', function () {
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                checked: '=ngModel',
                ngValue: '='
            },
            transclude: true,
            /*jshint multistr: true */
            template: ' \
                         \
                             \
                                 \
                                 \
                             \
                            <input type="radio" ng-model="checked" ng-value="ngValue"/> \
                             \
                        ',
            replace: true
        };
    });

    app.directive('checkbox', function(){
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                checked: '=ngModel'
            },
            transclude: true,
            /*jshint multistr: true */
            template: ' \
                         \
                             \
                                 \
                                 \
                             \
                            <input type="checkbox" ng-model="checked"/> \
                             \
                        ',
            replace: true
        };
    });
})(angular);

(function() {
    var _pauseFlag = false;
    var start, whole;

    function  onPlayerStateChange (event) {
        var video_id = event.target.getVideoData().video_id;

        if (event.data == YT.PlayerState.PLAYING){
                ga('send', 'event', 'videos', 'play', video_id);
                //thy video plays
                //reaffirm the pausal beast is not with us
                _pauseFlag = false;
                if (whole !== 'ended' && whole !== 'started') {
                    start = new Date().getTime();
                    whole = 'started';
                }
        }
        //should the video tire out and cease
        if (event.data == YT.PlayerState.ENDED){
            ga('send', 'event', 'videos', 'watch To end', video_id);
            if (whole === 'started') {
                var stop = new Date().getTime();
                var delta_s = (stop - start) / 1000;
                ga('send', 'event', 'videos', 'time tO end', video_id, Math.round(delta_s));
                whole = 'ended';
            }
        }
        //and should we tell it to halt, cease, heal.
        //confirm the pause has but one head and it flies not its flag
        //lo the pause event will spawn a many headed monster
        //with events overflowing
        if (event.data == YT.PlayerState.PAUSED && _pauseFlag === false){
            ga('send', 'event', 'videos', 'pause', video_id);
            //tell the monster it may have
            //but one head
            _pauseFlag = true;
        }
        //and should the monster think, before it doth play
        //after we command it to move
        if (event.data == YT.PlayerState.BUFFERING){
            ga('send', 'event', 'videos', 'bufferIng', video_id);
        }
        //and should it cue
        //for why not track this as well.
        if (event.data == YT.PlayerState.CUED){
            ga('send', 'event', 'videos', 'cueing', video_id);
        }

        if (event.data === YT.PlayerState.ENDED) {
            ga("send", "event", "unit", "watched video");
        }
    }
    window.onPlayerStateChange = onPlayerStateChange;
})();
<commit_msg>discard last answer if the given array has a different length of expected array, fixed #289<commit_after>(function (angular) {
    "use strict";

    var app = angular.module('lesson', [
        'directive.markdowneditor',
        'directive.codemirror',
        'ngRoute',
        'ngResource',
        'youtube',
        'django',
        'forum',
        'notes'
    ]);

    window.ga = window.ga || function(){ console.log(arguments); };

    var ACTIVITY_TEMPLATE_PATH = function(the_type){
        return STATIC_URL + '/templates/activity_'+ the_type + '.html';
    };

    app.controller('MainCtrl', ['$scope', 'LessonData', 'Answer', '$location', 'youtubePlayerApi',
        function ($scope, LessonData, Answer, $location, youtubePlayerApi) {

            youtubePlayerApi.events.onStateChange = function(event){
                window.onPlayerStateChange.call($scope.currentUnit, event);
                if (event.data === YT.PlayerState.ENDED) {
                    window.$scope = $scope;
                    $scope.nextStep();
                    if(!$scope.$$phase) {
                        $scope.$apply();
                    }
                }
            };

            $scope.section = $scope.section || 'video';

            $scope.selectUnit = function(unit) {
                $scope.currentUnit = unit;
                if(unit.video) {
                    $scope.section = 'video';
                    $scope.play(unit.video.youtube_id);
                } else {
                    $scope.section = 'activity';
                }
                $scope.selectActivity(0);
            };

            $scope.nextUnit = function() {
                var index = $scope.lesson.units.indexOf($scope.currentUnit);
                index++;

                if(index < $scope.lesson.units.length) {
                    $scope.selectUnit($scope.lesson.units[index]);
                }
                // e se não tiver nextUnit, faz o que?
            };

            $scope.play = function() {
                var youtube_id = $scope.currentUnit.video.youtube_id;
                $scope.section = 'video';

                youtubePlayerApi.loadPlayer().then(function(player){
                    if(player.getVideoData() &&
                        player.getVideoData().video_id === youtube_id) return;
                    player.cueVideoById(youtube_id);
                });
            };

            $scope.selectActivity = function(index) {
                function _newAnswer(){
                    $scope.answer = new Answer();
                    if(angular.isArray($scope.currentActivity.expected)) {
                        $scope.answer.given = $scope.currentActivity.expected.map(function(){});
                    }
                }

                if($scope.currentUnit.activities && $scope.currentUnit.activities.length) {
                    $scope.currentActivity = $scope.currentUnit.activities[index];
                    $scope.activityTemplateUrl = ACTIVITY_TEMPLATE_PATH($scope.currentActivity.type);

                    Answer.getLastGivenAnswer($scope.currentActivity.id)
                        .then(function(answer){
                            if (angular.isArray($scope.currentActivity.expected) &&
                                angular.isArray(answer.given) &&
                                answer.given.length === $scope.currentActivity.expected.length){

                                $scope.answer = answer;
                            } else {
                                _newAnswer();
                            }
                        })
                        .catch(_newAnswer);
                } else {
                    $scope.currentActivity = null;
                    $scope.activityTemplateUrl = null;
                }
            };

            $scope.sendAnswer = function() {
                $scope.answer.activity = $scope.currentActivity.id;
                $scope.answer.saveOrUpdate().then(function(d){
                    ga('send', 'event', 'activity', 'result', '', d.correct);
                });
                ga('send', 'event', 'activity', 'submit');
            };

            $scope.nextStep = function() {
                if($scope.section === 'video') {
                    if($scope.currentUnit.activities) {
                        $scope.section = 'activity';
                    } else {
                        $scope.nextUnit();
                    }
                } else {
                    if($scope.section === 'activity' && $scope.currentActivity.comment) {
                        $scope.section = 'comment';
                    } else {
                        var index = $scope.currentUnit.activities.indexOf($scope.currentActivity);
                        if(index+1 === $scope.currentUnit.activities.length) {
                            $scope.nextUnit();
                        } else {
                            $scope.selectActivity(index + 1);
                            $scope.section = 'activity';
                        }
                    }
                }
            };

            var start;
            $scope.$watch('currentUnit', function() {
                if(!$scope.lesson) return;
                // Changing Unit means unit starting
                if (start) {
                    var end = new Date().getTime();
                    ga('send', 'event', 'unit', 'time in unit',
                       $scope.lesson.course + ' - ' + $scope.lesson.name + ' - ' + $scope.currentUnit,
                       end - start);
                }
                ga('send', 'event', 'unit', 'start', $scope.lesson.course + ' - ' + $scope.lesson.name, $scope.currentUnit);
                start = new Date().getTime();
            });

            LessonData.then(function(lesson){
                $scope.lesson = lesson;

                var index = /\/(\d+)/.extract($location.path(), 1);
                index = parseInt(index, 10) - 1 || 0;
                $scope.selectUnit(lesson.units[index]);
                $scope.play();

                $scope.$on('$locationChangeSuccess', function (event, newLoc, oldLoc){
                   index = /\/(\d+)/.extract(newLoc, 1);
                   index = parseInt(index, 10) - 1 || 0;
                   $scope.selectUnit(lesson.units[index]);
                });
            });
        }
    ]);

    app.factory('Answer',['$resource', '$q',
        function($resource, $q){
            var resourceConfig = {
                'update': {'method': 'PUT'}
            };
            var Answer = $resource('/api/answer/:id', {'id':'@id'}, resourceConfig);

            Answer.prototype.saveOrUpdate = function() {
                return this.id > 0 ? this.$update() : this.$save();
            };

            Answer.getLastGivenAnswer = function(activity_id) {
                var deferred = $q.defer();
                var extractLatest = function (list) {
                    if(list.length > 0) {
                        deferred.resolve(list.pop());
                    } else {
                        deferred.reject();
                    }
                };
                Answer.query({'activity': activity_id}, extractLatest);
                return deferred.promise;
            };

            return Answer;
        }
    ]);

    app.factory('LessonData', ['$rootScope', '$q', '$resource', '$window',
        function($rootScope, $q, $resource, $window) {
            var Lesson = $resource('/api/lessons/:lessonId/');
            var Progress = $resource('/api/student_progress?unit__lesson=:lessonId');
            var deferred = $q.defer();

            Lesson.get({'lessonId': $window.lessonId}, function (lesson) {
                $rootScope.lesson = lesson;
                deferred.resolve(lesson);
            });

            Progress.query({'lessonId': $window.lessonId}, function (progress) {
                deferred.promise.then(function (lesson) {
                    for (var i = progress.length - 1; i >= 0; i--) {
                        var p = progress[i];
                        for (var j = lesson.units.length - 1; j >= 0; j--) {
                            if (lesson.units[j].id === p.unit) {
                                lesson.units[j].progress = p;
                            }
                        }
                    }
                });
            });

            return deferred.promise;
        }
    ]);

    app.directive('radio', function () {
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                checked: '=ngModel',
                ngValue: '='
            },
            transclude: true,
            /*jshint multistr: true */
            template: ' \
                         \
                             \
                                 \
                                 \
                             \
                            <input type="radio" ng-model="checked" ng-value="ngValue"/> \
                             \
                        ',
            replace: true
        };
    });

    app.directive('checkbox', function(){
        return {
            restrict: 'E',
            require: 'ngModel',
            scope: {
                checked: '=ngModel'
            },
            transclude: true,
            /*jshint multistr: true */
            template: ' \
                         \
                             \
                                 \
                                 \
                             \
                            <input type="checkbox" ng-model="checked"/> \
                             \
                        ',
            replace: true
        };
    });
})(angular);

(function() {
    var _pauseFlag = false;
    var start, whole;

    function  onPlayerStateChange (event) {
        var video_id = event.target.getVideoData().video_id;

        if (event.data == YT.PlayerState.PLAYING){
                ga('send', 'event', 'videos', 'play', video_id);
                //thy video plays
                //reaffirm the pausal beast is not with us
                _pauseFlag = false;
                if (whole !== 'ended' && whole !== 'started') {
                    start = new Date().getTime();
                    whole = 'started';
                }
        }
        //should the video tire out and cease
        if (event.data == YT.PlayerState.ENDED){
            ga('send', 'event', 'videos', 'watch To end', video_id);
            if (whole === 'started') {
                var stop = new Date().getTime();
                var delta_s = (stop - start) / 1000;
                ga('send', 'event', 'videos', 'time tO end', video_id, Math.round(delta_s));
                whole = 'ended';
            }
        }
        //and should we tell it to halt, cease, heal.
        //confirm the pause has but one head and it flies not its flag
        //lo the pause event will spawn a many headed monster
        //with events overflowing
        if (event.data == YT.PlayerState.PAUSED && _pauseFlag === false){
            ga('send', 'event', 'videos', 'pause', video_id);
            //tell the monster it may have
            //but one head
            _pauseFlag = true;
        }
        //and should the monster think, before it doth play
        //after we command it to move
        if (event.data == YT.PlayerState.BUFFERING){
            ga('send', 'event', 'videos', 'bufferIng', video_id);
        }
        //and should it cue
        //for why not track this as well.
        if (event.data == YT.PlayerState.CUED){
            ga('send', 'event', 'videos', 'cueing', video_id);
        }

        if (event.data === YT.PlayerState.ENDED) {
            ga("send", "event", "unit", "watched video");
        }
    }
    window.onPlayerStateChange = onPlayerStateChange;
})();
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * LayerSwitch component
 * Creates a LayerSwitch component
 * @author Meine Toonen
 */
Ext.define ("viewer.viewercontroller.openlayers.LoadMonitor",{
    extend: "viewer.viewercontroller.openlayers.OpenLayersComponent",
    config:{
        top:null,
        left:null
    },
    constructor: function (conf){
        viewer.viewercontroller.openlayers.LoadMonitor.superclass.constructor.call(this, conf);
        var map = this.viewerController.mapComponent.getMap().getFrameworkMap();
        this.frameworkComponent = new OpenLayers.Control.LoadingPanel();
        map.addControl(this.frameworkComponent);

        if(this.left && this.top){
            this.setPosition(this.top, this.left);
        }

        return this;
    },

    setPosition : function (top, left){
        var div = this.frameworkComponent.div;
        div.style.top = top + "px";
        div.style.left = left+ "px";
    },

    getExtComponents: function() {
        return [];
    }
});
<commit_msg>Comments corrected<commit_after>/*
 * Copyright (C) 2012 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * LoadMonitor component
 * Creates a LoadMonitor component for OpenLayers
 * @author Meine Toonen
 */
Ext.define ("viewer.viewercontroller.openlayers.LoadMonitor",{
    extend: "viewer.viewercontroller.openlayers.OpenLayersComponent",
    config:{
        top:null,
        left:null
    },

    constructor: function (conf){
        viewer.viewercontroller.openlayers.LoadMonitor.superclass.constructor.call(this, conf);
        // Make the control and add it to the openlayersmap
        var map = this.viewerController.mapComponent.getMap().getFrameworkMap();
        this.frameworkComponent = new OpenLayers.Control.LoadingPanel();
        map.addControl(this.frameworkComponent);

        if(this.left && this.top){
            this.setPosition(this.top, this.left);
        }

        return this;
    },

    // Set the position of the loadingpanel
    setPosition : function (top, left){
        var div = this.frameworkComponent.div;
        div.style.top = top + "px";
        div.style.left = left+ "px";
    },

    getExtComponents: function() {
        return [];
    }
});
<|endoftext|>