<commit_before>"use strict";

var $, ExclusionRulesOption, bgSettings, bgExclusions, BG,
KeyRe = /<(?:(?:a-(?:c-)?(?:m-)?|c-(?:m-)?|m-)(?:[A-Z][0-9A-Z]+|[a-z][0-9a-z]+|[^\s])|[A-Z][0-9A-Z]+|[a-z][0-9a-z]+)>|[^\s]/g,
__hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) {
  for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
  function Ctor() { this.constructor = child; } Ctor.prototype = parent.prototype;
  child.prototype = new Ctor(); child.__super__ = parent.prototype;
  return child;
};

$ = document.getElementById.bind(document);
BG = chrome.extension.getBackgroundPage();
bgSettings = BG.Settings;
bgExclusions = BG.Exclusions;
$("exclusionScrollBox").innerHTML = bgSettings.cache.exclusionTemplate;

function Option(element, onUpdated) {
  this.element = element;
  this.field = element.id;
  if (this.field in bgSettings.bufferToLoad) {
    this.onUpdated1 = onUpdated;
    onUpdated = this._onUpdated;
  }
  this.onUpdated = onUpdated.bind(this);
  this.previous = null;
  this.saved = true;
  this.fetch(bgSettings.get(this.field));
  Option.all.push(this);
}

Option.all = [];
Option.syncToFrontend = [];

Option.prototype._onUpdated = function() {
  this.onUpdated1();
  Settings.cache[this.field] = this.readValueFromElement();
};

Option.prototype.fetch = function() {
  this.populateElement(this.previous = bgSettings.get(this.field));
  this.saved = true;
};

Option.prototype.save = function() {
  var value = this.readValueFromElement(), previous = this.previous, notJSON = true;
  if (typeof value === "object") {
    value = JSON.stringify(value);
    previous = JSON.stringify(this.previous);
    if (value === previous) { return; }
    notJSON = false;
    if (value === JSON.stringify(bgSettings.defaults[this.field])) {
      value = bgSettings.defaults[this.field];
    } else {
      value = BG.JSON.parse(value);
    }
  } else if (value === previous) {
    return;
  }
  bgSettings.set(this.field, value);
  this.previous = value = bgSettings.get(this.field);
  this.saved = true;
  if (previous !== (notJSON ? value : JSON.stringify(value))) {
    this.populateElement(value);
  }
  if (this.field in bgSettings.bufferToLoad) {
    Option.syncToFrontend.push(this.field);
  }
};

Option.areJSONEqual = function(a, b) {
  return this.saved = JSON.stringify(a) === JSON.stringify(b);
};

function ExclusionRulesOption() {
  var _this = this;
  this.template = $('exclusionRuleTemplate').content.children[0];
  ExclusionRulesOption.__super__.constructor.apply(this, arguments);
  $("exclusionAddButton").addEventListener("click", function() { _this.addRule(null); });
  this.element.addEventListener("input", this.onUpdated);
  this.element.addEventListener("click", function(e) { _this.onRemoveRow(e); });
}
__extends(ExclusionRulesOption, Option);

ExclusionRulesOption.prototype.addRule = function(pattern) {
  var element, exclusionScrollBox;
  element = this.appendRule(this.element, {
    pattern: pattern || "",
    passKeys: ""
  });
  this.getPattern(element).focus();
  exclusionScrollBox = this.element.parentElement;
  exclusionScrollBox.scrollTop = exclusionScrollBox.scrollHeight;
  if (pattern) {
    this.onUpdated();
  }
  return element;
};

ExclusionRulesOption.prototype.populateElement = function(rules) {
  var frag = document.createDocumentFragment(), head;
  head = this.element.querySelector('tr');
  frag.appendChild(head);
  this.element.textContent = "";
  rules.forEach(this.appendRule.bind(this, frag));
  this.element.appendChild(frag);
};

ExclusionRulesOption.prototype.appendRule = function(list, rule) {
  var row;
  row = document.importNode(this.template, true);
  row.querySelector('.pattern').value = rule.pattern;
  row.querySelector('.passKeys').value = rule.passKeys;
  list.appendChild(row);
  return row;
};

ExclusionRulesOption.prototype.onRemoveRow = function(event) {
  if (!event.target.classList.contains("exclusionRemoveButton")) {
    return;
  }
  var row1 = event.target.parentNode.parentNode;
  if (row1.classList.contains("exclusionRuleInstance")) {
    row1.remove();
    this.onUpdated();
  }
};

ExclusionRulesOption.prototype.reChar = /^[\^\*]|[^\\][\$\(\)\*\+\?\[\]\{\|\}]/;
ExclusionRulesOption.prototype._escapeRe = /\\./g;
ExclusionRulesOption.prototype.readValueFromElement = function(part) {
  var element, passKeys, pattern, rules, _i, _len, _ref, passArr;
  rules = [];
  _ref = this.element.getElementsByClassName("exclusionRuleInstance");
  part = (part === true);
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
    element = _ref[_i];
    if (part && element.style.display === "none") {
      continue;
    }
    pattern = this.getPattern(element).value.trim();
    if (!pattern) {
      continue;
    }
    if (pattern[0] === ":" || element.style.display === "none") {}
    else if (this.reChar.test(pattern)) {
      pattern = pattern[0] === "^" ? pattern
        : (pattern.indexOf("://") === -1 ? "^http://" : "^") + pattern;
    } else {
      pattern = pattern.replace(this._escapeRe, "$&");
      pattern = (pattern.indexOf("://") === -1 ? ":http://" : ":") + pattern;
    }
    passKeys = this.getPassKeys(element).value;
    if (passKeys) {
      passArr = passKeys.match(KeyRe);
      passKeys = passArr ? (passArr.sort().join(" ") + " ") : "";
    }
    rules.push({
      pattern: pattern,
      passKeys: passKeys
    });
  }
  return rules;
};

ExclusionRulesOption.prototype.flatten = function(rule) {
  return (rule && rule.pattern) ? (rule.pattern + "\r" + rule.passKeys) : "";
};

ExclusionRulesOption.prototype.areEqual = Option.areJSONEqual;

ExclusionRulesOption.prototype.getPattern = function(element) {
  return element.querySelector(".pattern");
};

ExclusionRulesOption.prototype.getPassKeys = function(element) {
  return element.querySelector(".passKeys");
};

if (location.pathname.indexOf("/popup.html", location.pathname.length - 11) !== -1) {
chrome.tabs.query({currentWindow: true, active: true}, function(tab) {
  var exclusions, onUpdated, saveOptions, updateState, status = 0;

exclusions = {
  url: "",
  testers: null,
  init: function(url, element, onUpdated) {
    this.url = url;
    this.rebuildTesters();
    this.__proto__ = ExclusionRulesOption.prototype;
    ExclusionRulesOption.call(this, element, onUpdated);
    this.element.addEventListener("input", this.OnInput);
    this.init = null;
  },
  rebuildTesters: function() {
    var rules = bgSettings.get("exclusionRules")
      , ref = bgExclusions.testers = {}
      , ref2 = bgExclusions.rules, _i, _j, pattern;
    for (_i = rules.length, _j = 0; 0 <= --_i; ) {
      if (pattern = rules[_i].pattern) {
        ref[pattern] = ref2[_j++][0];
      }
    }
  },
  addRule: function() {
    this.__proto__.addRule.call(this, this.generateDefaultPattern());
  },
  populateElement: function(rules) {
    var element, elements, haveMatch, pattern, _i, _len;
    this.__proto__.populateElement.call(this, rules);
    elements = this.element.getElementsByClassName("exclusionRuleInstance");
    haveMatch = -1;
    for (_i = 0, _len = elements.length; _i < _len; _i++) {
      element = elements[_i];
      pattern = this.getPattern(element).value.trim();
      if (bgExclusions.testers[pattern](this.url)) {
        haveMatch = _i;
      } else {
        element.style.display = "none";
      }
    }
    if (haveMatch >= 0) {
      this.getPassKeys(elements[haveMatch]).focus();
    } else {
      this.addRule();
    }
  },
  OnInput: function(event) {
    var patternElement = event.target;
    if (!patternElement.classList.contains("pattern")) {
      return;
    }
    if (bgExclusions.getRe(patternElement.value)(exclusions.url)) {
      patternElement.title = patternElement.style.color = "";
    } else {
      patternElement.style.color = "red";
      patternElement.title = "Red text means that the pattern does not\nmatch the current URL.";
    }
  },
  generateDefaultPattern: function() {
    var url = this.url.lastIndexOf("https:", 0) === 0
      ? "^https?://" + this.url.split("/", 3)[2].replace(/\./g, "\\.") + "/"
      : /^[a-z]{3,}:\/\/./.test(this.url)
      ? ":" + (this.url.split("/", 3).join("/") + "/")
      : ":" + this.url;
    this.generateDefaultPattern = function() { return url; };
    return url;
  }
};

  tab = tab[0];
  var escapeRe = /[&<>]/g, escapeCallback = function(c, n) {
    n = c.charCodeAt(0);
    return (n === 60) ? "<" : (n === 62) ? ">" : "&";
  };
  updateState = function() {
    var pass = bgExclusions.getTemp(exclusions.url, exclusions.readValueFromElement(true));
    $("state").innerHTML = "Vimium++ will " + (pass
      ? "exclude: " + pass.replace(escapeRe, escapeCallback) + ""
      : pass !== null ? "be disabled" : "be enabled");
  };
  onUpdated = function() {
    if (status != 1) {
      status = 1;
      var btn = $("saveOptions");
      $("helpText").innerHTML = "Type Ctrl-Enter to save and close.";
      btn.removeAttribute("disabled");
      btn.textContent = "Save Changes";
    }
    if (!exclusions.init) {
      updateState();
    }
  };
  saveOptions = function() {
    var btn = $("saveOptions");
    if (btn.disabled) {
      return;
    }
    exclusions.save();
    btn.textContent = "Saved";
    btn.disabled = true;
    status = 0;
    // Here, since the popup page is showing, needIcon must be true.
    // Although the tab calls window.onfocus after this popup page closes,
    // it is too early for the tab to know new exclusion rules.
    BG.g_requestHandlers.SetIcon(tab.id, null, bgExclusions.getPattern(exclusions.url));
  };
  $("saveOptions").onclick = saveOptions;
  document.addEventListener("keyup", function(event) {
    if ((event.ctrlKey || event.metaKey) && event.keyCode === 13) {
      if (status === 1) {
        saveOptions();
      }
      setTimeout(window.close, 300);
    }
  });
  exclusions.init(bgSettings.urlForTab[tab.id] || tab.url, $("exclusionRules"), onUpdated);
  updateState();
  var link = $("optionsLink");
  link.href = bgSettings.CONST.OptionsPage;
  link.onclick = function(event) {
    BG.g_requestHandlers.focusOrLaunch({ url: this.href });
    event.preventDefault();
    event.stopImmediatePropagation();
    window.close();
  };
  window.onunload = function() {
    bgExclusions.testers = null;
  };
});

}
<commit_msg>options: fix a bug that redraw is always triggered when saving<commit_after>"use strict";

var $, ExclusionRulesOption, bgSettings, bgExclusions, BG,
KeyRe = /<(?:(?:a-(?:c-)?(?:m-)?|c-(?:m-)?|m-)(?:[A-Z][0-9A-Z]+|[a-z][0-9a-z]+|[^\s])|[A-Z][0-9A-Z]+|[a-z][0-9a-z]+)>|[^\s]/g,
__hasProp = Object.prototype.hasOwnProperty,
__extends = function(child, parent) {
  for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
  function Ctor() { this.constructor = child; } Ctor.prototype = parent.prototype;
  child.prototype = new Ctor(); child.__super__ = parent.prototype;
  return child;
};

$ = document.getElementById.bind(document);
BG = chrome.extension.getBackgroundPage();
bgSettings = BG.Settings;
bgExclusions = BG.Exclusions;
$("exclusionScrollBox").innerHTML = bgSettings.cache.exclusionTemplate;

function Option(element, onUpdated) {
  this.element = element;
  this.field = element.id;
  if (this.field in bgSettings.bufferToLoad) {
    this.onUpdated1 = onUpdated;
    onUpdated = this._onUpdated;
  }
  this.onUpdated = onUpdated.bind(this);
  this.previous = null;
  this.saved = true;
  this.fetch(bgSettings.get(this.field));
  Option.all.push(this);
}

Option.all = [];
Option.syncToFrontend = [];

Option.prototype._onUpdated = function() {
  this.onUpdated1();
  Settings.cache[this.field] = this.readValueFromElement();
};

Option.prototype.fetch = function() {
  this.populateElement(this.previous = bgSettings.get(this.field));
  this.saved = true;
};

Option.prototype.save = function() {
  var value = this.readValueFromElement(), previous = this.previous, notJSON = true;
  if (typeof value === "object") {
    value = JSON.stringify(value);
    previous = JSON.stringify(this.previous);
    if (value === previous) { return; }
    previous = value;
    notJSON = false;
    if (value === JSON.stringify(bgSettings.defaults[this.field])) {
      value = bgSettings.defaults[this.field];
    } else {
      value = BG.JSON.parse(value);
    }
  } else if (value === previous) {
    return;
  } else {
    previous = value;
  }
  bgSettings.set(this.field, value);
  this.previous = value = bgSettings.get(this.field);
  this.saved = true;
  if (previous !== (notJSON ? value : JSON.stringify(value))) {
    this.populateElement(value);
  }
  if (this.field in bgSettings.bufferToLoad) {
    Option.syncToFrontend.push(this.field);
  }
};

Option.areJSONEqual = function(a, b) {
  return this.saved = JSON.stringify(a) === JSON.stringify(b);
};

function ExclusionRulesOption() {
  var _this = this;
  this.template = $('exclusionRuleTemplate').content.children[0];
  ExclusionRulesOption.__super__.constructor.apply(this, arguments);
  $("exclusionAddButton").addEventListener("click", function() { _this.addRule(null); });
  this.element.addEventListener("input", this.onUpdated);
  this.element.addEventListener("click", function(e) { _this.onRemoveRow(e); });
}
__extends(ExclusionRulesOption, Option);

ExclusionRulesOption.prototype.addRule = function(pattern) {
  var element, exclusionScrollBox;
  element = this.appendRule(this.element, {
    pattern: pattern || "",
    passKeys: ""
  });
  this.getPattern(element).focus();
  exclusionScrollBox = this.element.parentElement;
  exclusionScrollBox.scrollTop = exclusionScrollBox.scrollHeight;
  if (pattern) {
    this.onUpdated();
  }
  return element;
};

ExclusionRulesOption.prototype.populateElement = function(rules) {
  var frag = document.createDocumentFragment(), head;
  head = this.element.querySelector('tr');
  frag.appendChild(head);
  this.element.textContent = "";
  rules.forEach(this.appendRule.bind(this, frag));
  this.element.appendChild(frag);
};

ExclusionRulesOption.prototype.appendRule = function(list, rule) {
  var row;
  row = document.importNode(this.template, true);
  row.querySelector('.pattern').value = rule.pattern;
  row.querySelector('.passKeys').value = rule.passKeys;
  list.appendChild(row);
  return row;
};

ExclusionRulesOption.prototype.onRemoveRow = function(event) {
  if (!event.target.classList.contains("exclusionRemoveButton")) {
    return;
  }
  var row1 = event.target.parentNode.parentNode;
  if (row1.classList.contains("exclusionRuleInstance")) {
    row1.remove();
    this.onUpdated();
  }
};

ExclusionRulesOption.prototype.reChar = /^[\^\*]|[^\\][\$\(\)\*\+\?\[\]\{\|\}]/;
ExclusionRulesOption.prototype._escapeRe = /\\./g;
ExclusionRulesOption.prototype.readValueFromElement = function(part) {
  var element, passKeys, pattern, rules, _i, _len, _ref, passArr;
  rules = [];
  _ref = this.element.getElementsByClassName("exclusionRuleInstance");
  part = (part === true);
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
    element = _ref[_i];
    if (part && element.style.display === "none") {
      continue;
    }
    pattern = this.getPattern(element).value.trim();
    if (!pattern) {
      continue;
    }
    if (pattern[0] === ":" || element.style.display === "none") {}
    else if (this.reChar.test(pattern)) {
      pattern = pattern[0] === "^" ? pattern
        : (pattern.indexOf("://") === -1 ? "^http://" : "^") + pattern;
    } else {
      pattern = pattern.replace(this._escapeRe, "$&");
      pattern = (pattern.indexOf("://") === -1 ? ":http://" : ":") + pattern;
    }
    passKeys = this.getPassKeys(element).value;
    if (passKeys) {
      passArr = passKeys.match(KeyRe);
      passKeys = passArr ? (passArr.sort().join(" ") + " ") : "";
    }
    rules.push({
      pattern: pattern,
      passKeys: passKeys
    });
  }
  return rules;
};

ExclusionRulesOption.prototype.flatten = function(rule) {
  return (rule && rule.pattern) ? (rule.pattern + "\r" + rule.passKeys) : "";
};

ExclusionRulesOption.prototype.areEqual = Option.areJSONEqual;

ExclusionRulesOption.prototype.getPattern = function(element) {
  return element.querySelector(".pattern");
};

ExclusionRulesOption.prototype.getPassKeys = function(element) {
  return element.querySelector(".passKeys");
};

if (location.pathname.indexOf("/popup.html", location.pathname.length - 11) !== -1) {
chrome.tabs.query({currentWindow: true, active: true}, function(tab) {
  var exclusions, onUpdated, saveOptions, updateState, status = 0;

exclusions = {
  url: "",
  testers: null,
  init: function(url, element, onUpdated) {
    this.url = url;
    this.rebuildTesters();
    this.__proto__ = ExclusionRulesOption.prototype;
    ExclusionRulesOption.call(this, element, onUpdated);
    this.element.addEventListener("input", this.OnInput);
    this.init = null;
  },
  rebuildTesters: function() {
    var rules = bgSettings.get("exclusionRules")
      , ref = bgExclusions.testers = {}
      , ref2 = bgExclusions.rules, _i, _j, pattern;
    for (_i = rules.length, _j = 0; 0 <= --_i; ) {
      if (pattern = rules[_i].pattern) {
        ref[pattern] = ref2[_j++][0];
      }
    }
  },
  addRule: function() {
    this.__proto__.addRule.call(this, this.generateDefaultPattern());
  },
  populateElement: function(rules) {
    var element, elements, haveMatch, pattern, _i, _len;
    this.__proto__.populateElement.call(this, rules);
    elements = this.element.getElementsByClassName("exclusionRuleInstance");
    haveMatch = -1;
    for (_i = 0, _len = elements.length; _i < _len; _i++) {
      element = elements[_i];
      pattern = this.getPattern(element).value.trim();
      if (bgExclusions.testers[pattern](this.url)) {
        haveMatch = _i;
      } else {
        element.style.display = "none";
      }
    }
    if (haveMatch >= 0) {
      this.getPassKeys(elements[haveMatch]).focus();
    } else {
      this.addRule();
    }
  },
  OnInput: function(event) {
    var patternElement = event.target;
    if (!patternElement.classList.contains("pattern")) {
      return;
    }
    if (bgExclusions.getRe(patternElement.value)(exclusions.url)) {
      patternElement.title = patternElement.style.color = "";
    } else {
      patternElement.style.color = "red";
      patternElement.title = "Red text means that the pattern does not\nmatch the current URL.";
    }
  },
  generateDefaultPattern: function() {
    var url = this.url.lastIndexOf("https:", 0) === 0
      ? "^https?://" + this.url.split("/", 3)[2].replace(/\./g, "\\.") + "/"
      : /^[a-z]{3,}:\/\/./.test(this.url)
      ? ":" + (this.url.split("/", 3).join("/") + "/")
      : ":" + this.url;
    this.generateDefaultPattern = function() { return url; };
    return url;
  }
};

  tab = tab[0];
  var escapeRe = /[&<>]/g, escapeCallback = function(c, n) {
    n = c.charCodeAt(0);
    return (n === 60) ? "<" : (n === 62) ? ">" : "&";
  };
  updateState = function() {
    var pass = bgExclusions.getTemp(exclusions.url, exclusions.readValueFromElement(true));
    $("state").innerHTML = "Vimium++ will " + (pass
      ? "exclude: " + pass.replace(escapeRe, escapeCallback) + ""
      : pass !== null ? "be disabled" : "be enabled");
  };
  onUpdated = function() {
    if (status != 1) {
      status = 1;
      var btn = $("saveOptions");
      $("helpText").innerHTML = "Type Ctrl-Enter to save and close.";
      btn.removeAttribute("disabled");
      btn.textContent = "Save Changes";
    }
    if (!exclusions.init) {
      updateState();
    }
  };
  saveOptions = function() {
    var btn = $("saveOptions");
    if (btn.disabled) {
      return;
    }
    exclusions.save();
    btn.textContent = "Saved";
    btn.disabled = true;
    status = 0;
    // Here, since the popup page is showing, needIcon must be true.
    // Although the tab calls window.onfocus after this popup page closes,
    // it is too early for the tab to know new exclusion rules.
    BG.g_requestHandlers.SetIcon(tab.id, null, bgExclusions.getPattern(exclusions.url));
  };
  $("saveOptions").onclick = saveOptions;
  document.addEventListener("keyup", function(event) {
    if ((event.ctrlKey || event.metaKey) && event.keyCode === 13) {
      if (status === 1) {
        saveOptions();
      }
      setTimeout(window.close, 300);
    }
  });
  exclusions.init(bgSettings.urlForTab[tab.id] || tab.url, $("exclusionRules"), onUpdated);
  updateState();
  var link = $("optionsLink");
  link.href = bgSettings.CONST.OptionsPage;
  link.onclick = function(event) {
    BG.g_requestHandlers.focusOrLaunch({ url: this.href });
    event.preventDefault();
    event.stopImmediatePropagation();
    window.close();
  };
  window.onunload = function() {
    bgExclusions.testers = null;
  };
});

}
<|endoftext|>


<commit_before>define(['app/models/key'],
    /**
     * Keys Controller
     *
     * @returns Class
     */
    function(Key) {
        return Ember.ArrayController.extend({

            /**
             *
             *  Properties
             *
             */

            content: [],
            keyRequest: false,
            keyResponse: false,

            loading: false,
            creatingKey: false,
            renamingKey: false,
            associatingKey: false,
            gettingPublicKey: false,
            gettingPrivateKey: false,
            disassociatingKey: false,
            settingDefaultKey: false,

            /**
             *
             *  Initialization
             *
             */

            load: function() {
                var that = this;
                this.set('loading', true);
                $.getJSON('/keys', function(keys) {
                    that._setContent(keys);
                    that._sendKeyResponse();
                }).error(function() {
                    that._reload();
                }).complete(function() {
                    that.set('loading', false);
                });
            }.on('init'),

            /**
             *
             *  Observers
             *
             */

            keyRequestObserver: function() {
                if (!this.loading && this.keyRequest) {
                    this._sendKeyResponse();
                }
            }.observes('keyRequest'),

            /**
             *
             *  Methods
             *
             */

            renameKey: function(name, newName, callback) {
                var that = this;
                this.set('renamingKey', true);
                $.ajax({
                    url: '/keys/' + name,
                    type: 'PUT',
                    contentType: 'application/json',
                    data: JSON.stringify({'newName': newName}),
                    success: function() {
                        that._renameKey(name, newName);
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to rename key');
                    },
                    complete: function() {
                        that.set('renamingKey', false);
                    }
                });
            },

            deleteKey: function(name, callback) {
                var that = this;
                this.set('deletingKey', true);
                $.ajax({
                    url: '/keys/' + name,
                    type: 'DELETE',
                    success: function() {
                        that._deleteKey(name);
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to delete key');
                    },
                    complete: function() {
                        that.set('deletingKey', false);
                    }
                });
            },

            setDefaultKey: function(name, callback) {
                this.set('settingDefaultKey', true);
                $.ajax({
                    url: '/keys/' + name,
                    type: 'POST',
                    success: function() {
                        that._setDefaultKey(name);
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to set default key');
                    },
                    complete: function() {
                        Mist.keysController.set('settingDefaultKey', false);
                    }
                });
            },

            associateKey: function(keyName, backendId, machineId, host, callback) {
                this.set('associatingKey', true);
                $.ajax({
                    url: '/backends/' + backendId + '/machines/' + machineId + '/keys/' + keyName,
                    type: 'PUT',
                    contentType: 'application/json',
                    data: JSON.stringify({'host': host}),
                    success: function() {
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to associate key');
                    },
                    complete: function() {
                        Mist.keysController.set('associatingKey', false);
                    }
                });
            },

            disassociateKey: function(keyName, backendId, machineId, host, callback) {
                this.set('disassociatingKey', true);
                $.ajax({
                    url: '/backends/' + backendId + '/machines/' + machineId + '/keys/' + keyName,
                    type: 'DELETE',
                    contentType: 'application/json',
                    data: JSON.stringify({'host': host}),
                    success: function() {
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to disassociate key');
                    },
                    complete: function() {
                        Mist.keysController.set('disassociatingKey', false);
                    }
                });
            },

            getPrivateKey: function(name, callback) {
                this.set('gettingPrivateKey', true);
                $.getJSON('/keys/' + name + '/private', function(key) {
                    if (callback) callback(key);
                }).error(function() {
                    Mist.notificationController.notify('Failed to get private key');
                }).complete(function() {
                    Mist.keysController.set('gettingPrivateKey', true);
                });
            },

            getPublicKey: function(name, callback) {
                this.set('gettingPublicKey', true);
                $.getJSON('/keys/' + name + '/public', function(key) {
                    if (callback) callback(key);
                }).error(function() {
                    Mist.notificationController.notify('Failed to get public key');
                }).complete(function() {
                    Mist.keysController.set('gettingPublicKey', true);
                });
            },

            getKeyByName: function(name) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name == name) {
                        return content[k];
                    }
                }
            },

            getKeyByUrlName: function(name) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].id == name) {
                        return content[k];
                    }
                }
            },

            getSelectedKeysCount: function() {
                var counter = 0;
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].selected) ++counter;
                }
                return counter;
            },

            getSelectedKeyName: function() {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].selected) return content[k].name;
                }
            },

            /**
             *
             *  Psudo-Private Methods
             *
             */

            _setContent: function(keys) {
                var newKeys = [];
                var keysLength = keys.length;
                for (var k = 0; k < keysLength; ++k) {
                    newKeys.push(Key.create(keys[k]));
                }
                this.set('content', newKeys);
            },

            _sendKeyResponse: function() {
                if (this.keyRequest) {
                    this.set('keyResponse', this.getKeyByUrlName(this.keyRequest));
                    this.set('keyRequest', false);
                }
            },

            _reload: function() {
                Ember.run.later(this, function() {
                    this.load();
                }, 5000);
            },

            _renameKey: function(name, newName) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name == name) {
                        content[k].set('name', newName);
                        content[k].set('id', newName.replace(/ /g,''));
                        return;
                    }
                }
            },

            _deleteKey: function(name) {
                var newKeys = [];
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name != name) {
                        newKeys.push(content[k]);
                    }
                }
                this.set('content', newKeys);
            },

            _setDefaultKey: function(name) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name == name) {
                        content[k].set('default_key', true);
                    } else {
                        content[k].set('default_key', false);
                    }
                }
            }
        });
    }
);
<commit_msg>Fix setDefaultKey<commit_after>define(['app/models/key'],
    /**
     * Keys Controller
     *
     * @returns Class
     */
    function(Key) {
        return Ember.ArrayController.extend({

            /**
             *
             *  Properties
             *
             */

            content: [],
            keyRequest: false,
            keyResponse: false,

            loading: false,
            creatingKey: false,
            renamingKey: false,
            associatingKey: false,
            gettingPublicKey: false,
            gettingPrivateKey: false,
            disassociatingKey: false,
            settingDefaultKey: false,

            /**
             *
             *  Initialization
             *
             */

            load: function() {
                var that = this;
                this.set('loading', true);
                $.getJSON('/keys', function(keys) {
                    that._setContent(keys);
                    that._sendKeyResponse();
                }).error(function() {
                    that._reload();
                }).complete(function() {
                    that.set('loading', false);
                });
            }.on('init'),

            /**
             *
             *  Observers
             *
             */

            keyRequestObserver: function() {
                if (!this.loading && this.keyRequest) {
                    this._sendKeyResponse();
                }
            }.observes('keyRequest'),

            /**
             *
             *  Methods
             *
             */

            renameKey: function(name, newName, callback) {
                var that = this;
                this.set('renamingKey', true);
                $.ajax({
                    url: '/keys/' + name,
                    type: 'PUT',
                    contentType: 'application/json',
                    data: JSON.stringify({'newName': newName}),
                    success: function() {
                        that._renameKey(name, newName);
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to rename key');
                    },
                    complete: function() {
                        that.set('renamingKey', false);
                    }
                });
            },

            deleteKey: function(name, callback) {
                var that = this;
                this.set('deletingKey', true);
                $.ajax({
                    url: '/keys/' + name,
                    type: 'DELETE',
                    success: function() {
                        that._deleteKey(name);
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to delete key');
                    },
                    complete: function() {
                        that.set('deletingKey', false);
                    }
                });
            },

            setDefaultKey: function(name, callback) {
                var that = this;
                this.set('settingDefaultKey', true);
                $.ajax({
                    url: '/keys/' + name,
                    type: 'POST',
                    success: function() {
                        that._setDefaultKey(name);
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to set default key');
                    },
                    complete: function() {
                        that.set('settingDefaultKey', false);
                    }
                });
            },

            associateKey: function(keyName, backendId, machineId, host, callback) {
                this.set('associatingKey', true);
                $.ajax({
                    url: '/backends/' + backendId + '/machines/' + machineId + '/keys/' + keyName,
                    type: 'PUT',
                    contentType: 'application/json',
                    data: JSON.stringify({'host': host}),
                    success: function() {
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to associate key');
                    },
                    complete: function() {
                        Mist.keysController.set('associatingKey', false);
                    }
                });
            },

            disassociateKey: function(keyName, backendId, machineId, host, callback) {
                this.set('disassociatingKey', true);
                $.ajax({
                    url: '/backends/' + backendId + '/machines/' + machineId + '/keys/' + keyName,
                    type: 'DELETE',
                    contentType: 'application/json',
                    data: JSON.stringify({'host': host}),
                    success: function() {
                        if (callback) callback();
                    },
                    error: function() {
                        Mist.notificationController.notify('Failed to disassociate key');
                    },
                    complete: function() {
                        Mist.keysController.set('disassociatingKey', false);
                    }
                });
            },

            getPrivateKey: function(name, callback) {
                this.set('gettingPrivateKey', true);
                $.getJSON('/keys/' + name + '/private', function(key) {
                    if (callback) callback(key);
                }).error(function() {
                    Mist.notificationController.notify('Failed to get private key');
                }).complete(function() {
                    Mist.keysController.set('gettingPrivateKey', true);
                });
            },

            getPublicKey: function(name, callback) {
                this.set('gettingPublicKey', true);
                $.getJSON('/keys/' + name + '/public', function(key) {
                    if (callback) callback(key);
                }).error(function() {
                    Mist.notificationController.notify('Failed to get public key');
                }).complete(function() {
                    Mist.keysController.set('gettingPublicKey', true);
                });
            },

            getKeyByName: function(name) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name == name) {
                        return content[k];
                    }
                }
            },

            getKeyByUrlName: function(name) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].id == name) {
                        return content[k];
                    }
                }
            },

            getSelectedKeysCount: function() {
                var counter = 0;
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].selected) ++counter;
                }
                return counter;
            },

            getSelectedKeyName: function() {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].selected) return content[k].name;
                }
            },

            /**
             *
             *  Psudo-Private Methods
             *
             */

            _setContent: function(keys) {
                var newKeys = [];
                var keysLength = keys.length;
                for (var k = 0; k < keysLength; ++k) {
                    newKeys.push(Key.create(keys[k]));
                }
                this.set('content', newKeys);
            },

            _sendKeyResponse: function() {
                if (this.keyRequest) {
                    this.set('keyResponse', this.getKeyByUrlName(this.keyRequest));
                    this.set('keyRequest', false);
                }
            },

            _reload: function() {
                Ember.run.later(this, function() {
                    this.load();
                }, 5000);
            },

            _renameKey: function(name, newName) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name == name) {
                        content[k].set('name', newName);
                        content[k].set('id', newName.replace(/ /g,''));
                        return;
                    }
                }
            },

            _deleteKey: function(name) {
                var newKeys = [];
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name != name) {
                        newKeys.push(content[k]);
                    }
                }
                this.set('content', newKeys);
            },

            _setDefaultKey: function(name) {
                var content = this.content;
                var contentLength = this.content.length;
                for (var k = 0; k < contentLength; ++k) {
                    if (content[k].name == name) {
                        content[k].set('default_key', true);
                    } else {
                        content[k].set('default_key', false);
                    }
                }
            }
        });
    }
);
<|endoftext|>


<commit_before>
var MainMenu = function(node) {

	for (var i=0, il=sofia.menuComponents.length; i<il; i++) {
		var component = new window[sofia.menuComponents[i]](node);
	}

	var ext = {

	};

	ext = $.extend(true, ext, EventEmitter);
	return ext;

}

var MainLogo = function(node) {
	var logo = $('

')
					.appendTo(node)
					.on('click', logoClick);

	function logoClick() {

	}

	return logo;
}
sofia.menuComponents.push('MainLogo');

var MainSearchBox = function(node) {

	var searchBox = $('
' +
							'<input type="search" placeholder="Search" id="main-search-input" />' +
							'<input type="button" id="main-search-button" value="" />' +
					'
')
			.appendTo(node),

		searchInput = searchBox.find('#main-search-input')
								.on('keypress', checkInput),
		searchButton = searchBox.find('#main-search-button')
								.on('click', doMainSearch);

	function checkInput(e) {
		if (e.which == 13) {
			doMainSearch();
		}
	}

	function doMainSearch(e) {

		// do a search
		var searchString = searchInput.val(),
			appSettings = app.windowManager.getSettings(),
			searchWindow = null,
			firstTextWindow = null;

		for (var i=0,il=appSettings.length; i<il; i++) {
			var settings = appSettings[i];

			// first text
			if (settings.type == 'ScrollerWindow' && firstTextWindow == null) {
				firstTextWindow = settings;
			}

			// first search
			if (settings.type == 'SearchWindow' && searchWindow == null) {
				searchWindow = settings;
			}
		}

		//if (searchWindow == null) {

			// search based on first open window
			textid = firstTextWindow.data.textid;
			app.windowManager.add('SearchWindow', {searchString: searchString, textid: textid});

		//} else {

			// restart search

		//	console.log( searchWindow );

		//}

		searchBox.val('');

	}

	return searchBox;

}
sofia.menuComponents.push('MainSearchBox');

var AddWindowButton = function(node) {

	var addButton = $('

')
					.appendTo(node)
					.on('click', buttonClick),
		buttonMenu = $('

')
					.appendTo($('body'))

	function buttonClick(e) {

		if (buttonMenu.is(':visible')) {
			buttonMenu.hide();
		} else {
			buttonMenu.show();
		}
	}

	var windowTools = [
		{type: 'ScrollerWindow', label: 'Bible', data: {'textid':'eng_kjv','sectionid':'JN1','fragmentid':'JN1_10'}},
		{type: 'MapsWindow', label: 'Maps', data: {'latitude': 31.7833, 'longitude': 35.2167}},
		{type: 'SearchWindow', label: 'Search', data: {}},
		{type: 'VideoWindow', label: 'Video', data: {}},
		{type: 'AudioWindow', label: 'Audio', data: {}},
		{type: 'PicturesWindow', label: 'Pictures', data: {}},
	];

	for (var x in windowTools) {
		var tool = windowTools[x];
		// ADD Button
		var addButton = $('
' + tool.label + '
')
					.appendTo(buttonMenu)
					.data('init', tool);

	}

	buttonMenu.on('click', '.window-add', function() {

		var label = $(this),
			settings = label.data('init');

		app.windowManager.add(settings.type, settings.data);

		buttonMenu.hide();
		//windowManager.trigger('settingschange',{});
	});

	return addButton;

}
sofia.menuComponents.push('AddWindowButton');

var ConfigButton = function(node) {
	var configButton = $('

')
					.appendTo(node)
					.on('click', buttonClick),
		configMenu = $('

')
					.appendTo($('body'))

	function buttonClick(e) {

		if (configMenu.is(':visible')) {
			configMenu.hide();
		} else {
			configMenu.show();
		}
	}

}
sofia.menuComponents.push('ConfigButton');

var FullScreenButton = function(node) {

	var
		el = document.body,
		fullscreenButton = $('

')
			.appendTo(node)
			.on('click', toggleFullscreen),

		isFullscreen = false;

	function toggleFullscreen() {
		if (fullScreenApi.isFullScreen()) {
			exitFullscreen();
		} else {
			enterFullscreen();
		}
	}

	function enterFullscreen() {
		fullScreenApi.requestFullScreen(el);
	}

	function exitFullscreen() {
		fullScreenApi.cancelFullScreen();

	}

	return fullscreenButton;
};
sofia.menuComponents.push('FullScreenButton');

(function() {
    var
        fullScreenApi = {
            supportsFullScreen: false,
            isFullScreen: function() { return false; },
            requestFullScreen: function() {},
            cancelFullScreen: function() {},
            fullScreenEventName: '',
            prefix: ''
        },
        browserPrefixes = 'webkit moz o ms khtml'.split(' ');

    // check for native support
    if (typeof document.cancelFullScreen != 'undefined') {
        fullScreenApi.supportsFullScreen = true;
    } else {
        // check for fullscreen support by vendor prefix
        for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
            fullScreenApi.prefix = browserPrefixes[i];

            if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) {
                fullScreenApi.supportsFullScreen = true;

                break;
            }
        }
    }

    // update methods to do something useful
    if (fullScreenApi.supportsFullScreen) {
        fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';

        fullScreenApi.isFullScreen = function() {
            switch (this.prefix) {
                case '':
                    return document.fullScreen;
                case 'webkit':
                    return document.webkitIsFullScreen;
                default:
                    return document[this.prefix + 'FullScreen'];
            }
        }
        fullScreenApi.requestFullScreen = function(el) {
            return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
        }
        fullScreenApi.cancelFullScreen = function(el) {
            return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
        }
    }

    // jQuery plugin
    if (typeof jQuery != 'undefined') {
        jQuery.fn.requestFullScreen = function() {

            return this.each(function() {
                if (fullScreenApi.supportsFullScreen) {
                    fullScreenApi.requestFullScreen(this);
                }
            });
        };
    }

    // export api
    window.fullScreenApi = fullScreenApi;
})();

<commit_msg>moved namespaces<commit_after>
var MainMenu = function(node) {

	for (var i=0, il=sofia.menuComponents.length; i<il; i++) {
		var component = new window[sofia.menuComponents[i]](node);
	}

	var ext = {

	};

	ext = $.extend(true, ext, EventEmitter);
	return ext;

}

var MainLogo = function(node) {
	var logo = $('

')
					.appendTo(node)
					.on('click', logoClick);

	function logoClick() {

	}

	return logo;
}
sofia.menuComponents.push('MainLogo');

var MainSearchBox = function(node) {

	var searchBox = $('
' +
							'<input type="search" placeholder="Search" id="main-search-input" />' +
							'<input type="button" id="main-search-button" value="" />' +
					'
')
			.appendTo(node),

		searchInput = searchBox.find('#main-search-input')
								.on('keypress', checkInput),
		searchButton = searchBox.find('#main-search-button')
								.on('click', doMainSearch);

	function checkInput(e) {
		if (e.which == 13) {
			doMainSearch();
		}
	}

	function doMainSearch(e) {

		// do a search
		var searchString = searchInput.val(),
			appSettings = app.windowManager.getSettings(),
			searchWindow = null,
			firstTextWindow = null;

		for (var i=0,il=appSettings.length; i<il; i++) {
			var settings = appSettings[i];

			// first text
			if (settings.type == 'ScrollerWindow' && firstTextWindow == null) {
				firstTextWindow = settings;
			}

			// first search
			if (settings.type == 'SearchWindow' && searchWindow == null) {
				searchWindow = settings;
			}
		}

		//if (searchWindow == null) {

			// search based on first open window
			textid = firstTextWindow.data.textid;
			sofia.app.windowManager.add('SearchWindow', {searchString: searchString, textid: textid});

		//} else {

			// restart search

		//	console.log( searchWindow );

		//}

		searchBox.val('');

	}

	return searchBox;

}
sofia.menuComponents.push('MainSearchBox');

var AddWindowButton = function(node) {

	var addButton = $('

')
					.appendTo(node)
					.on('click', buttonClick),
		buttonMenu = $('

')
					.appendTo($('body'))

	function buttonClick(e) {

		if (buttonMenu.is(':visible')) {
			buttonMenu.hide();
		} else {
			buttonMenu.show();
		}
	}

	var windowTools = [
		{type: 'ScrollerWindow', label: 'Bible', data: {'textid':'eng_kjv','sectionid':'JN1','fragmentid':'JN1_10'}},
		{type: 'MapsWindow', label: 'Maps', data: {'latitude': 31.7833, 'longitude': 35.2167}},
		{type: 'SearchWindow', label: 'Search', data: {}},
		{type: 'VideoWindow', label: 'Video', data: {}},
		{type: 'AudioWindow', label: 'Audio', data: {}},
		{type: 'PicturesWindow', label: 'Pictures', data: {}},
	];

	for (var x in windowTools) {
		var tool = windowTools[x];
		// ADD Button
		var addButton = $('
' + tool.label + '
')
					.appendTo(buttonMenu)
					.data('init', tool);

	}

	buttonMenu.on('click', '.window-add', function() {

		var label = $(this),
			settings = label.data('init');

		sofia.app.windowManager.add(settings.type, settings.data);

		buttonMenu.hide();
		//windowManager.trigger('settingschange',{});
	});

	return addButton;

}
sofia.menuComponents.push('AddWindowButton');

var ConfigButton = function(node) {
	var configButton = $('

')
					.appendTo(node)
					.on('click', buttonClick),
		configMenu = $('

')
					.appendTo($('body'))

	function buttonClick(e) {

		if (configMenu.is(':visible')) {
			configMenu.hide();
		} else {
			configMenu.show();
		}
	}

}
sofia.menuComponents.push('ConfigButton');

var FullScreenButton = function(node) {

	var
		el = document.body,
		fullscreenButton = $('

')
			.appendTo(node)
			.on('click', toggleFullscreen),

		isFullscreen = false;

	function toggleFullscreen() {
		if (fullScreenApi.isFullScreen()) {
			exitFullscreen();
		} else {
			enterFullscreen();
		}
	}

	function enterFullscreen() {
		fullScreenApi.requestFullScreen(el);
	}

	function exitFullscreen() {
		fullScreenApi.cancelFullScreen();

	}

	return fullscreenButton;
};
sofia.menuComponents.push('FullScreenButton');

(function() {
    var
        fullScreenApi = {
            supportsFullScreen: false,
            isFullScreen: function() { return false; },
            requestFullScreen: function() {},
            cancelFullScreen: function() {},
            fullScreenEventName: '',
            prefix: ''
        },
        browserPrefixes = 'webkit moz o ms khtml'.split(' ');

    // check for native support
    if (typeof document.cancelFullScreen != 'undefined') {
        fullScreenApi.supportsFullScreen = true;
    } else {
        // check for fullscreen support by vendor prefix
        for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
            fullScreenApi.prefix = browserPrefixes[i];

            if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) {
                fullScreenApi.supportsFullScreen = true;

                break;
            }
        }
    }

    // update methods to do something useful
    if (fullScreenApi.supportsFullScreen) {
        fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';

        fullScreenApi.isFullScreen = function() {
            switch (this.prefix) {
                case '':
                    return document.fullScreen;
                case 'webkit':
                    return document.webkitIsFullScreen;
                default:
                    return document[this.prefix + 'FullScreen'];
            }
        }
        fullScreenApi.requestFullScreen = function(el) {
            return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
        }
        fullScreenApi.cancelFullScreen = function(el) {
            return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
        }
    }

    // jQuery plugin
    if (typeof jQuery != 'undefined') {
        jQuery.fn.requestFullScreen = function() {

            return this.each(function() {
                if (fullScreenApi.supportsFullScreen) {
                    fullScreenApi.requestFullScreen(this);
                }
            });
        };
    }

    // export api
    window.fullScreenApi = fullScreenApi;
})();

<|endoftext|>


<commit_before>
var _           = require('lodash'),
    when        = require('when'),
    errors      = require('../errors'),
    api         = require('../api'),
    loader      = require('./loader'),
    // Holds the available apps
    availableApps = {};

function getInstalledApps() {
    return api.settings.read({context: {internal: true}, key: 'installedApps'}).then(function (response) {
        var installed = response.settings[0];

        installed.value = installed.value || '[]';

        try {
            installed = JSON.parse(installed.value);
        } catch (e) {
            return when.reject(e);
        }

        return installed;
    });
}

function saveInstalledApps(installedApps) {
    return getInstalledApps().then(function (currentInstalledApps) {
        var updatedAppsInstalled = _.uniq(installedApps.concat(currentInstalledApps));

        return api.settings.edit({context: {internal: true}, key: 'installedApps'}, updatedAppsInstalled);
    });
}

module.exports = {
    init: function () {
        var appsToLoad;

        try {
            // We have to parse the value because it's a string
            api.settings.read({context: {internal: true}, key: 'activeApps'}).then(function (response) {
                var aApps = response.settings[0];

                appsToLoad = JSON.parse(aApps.value) || [];
            });
        } catch (e) {
            errors.logError(
                'Failed to parse activeApps setting value: ' + e.message,
                'Your apps will not be loaded.',
                'Check your settings table for typos in the activeApps value. It should look like: ["app-1", "app2"] (double quotes required).'
            );
            return when.resolve();
        }

        // Grab all installed apps, install any not already installed that are in appsToLoad.
        return getInstalledApps().then(function (installedApps) {
            var loadedApps = {},
                recordLoadedApp = function (name, loadedApp) {
                    // After loading the app, add it to our hash of loaded apps
                    loadedApps[name] = loadedApp;

                    return when.resolve(loadedApp);
                },
                loadPromises = _.map(appsToLoad, function (app) {
                    // If already installed, just activate the app
                    if (_.contains(installedApps, app)) {
                        return loader.activateAppByName(app).then(function (loadedApp) {
                            return recordLoadedApp(app, loadedApp);
                        });
                    }

                    // Install, then activate the app
                    return loader.installAppByName(app).then(function () {
                        return loader.activateAppByName(app);
                    }).then(function (loadedApp) {
                        return recordLoadedApp(app, loadedApp);
                    });
                });

            return when.all(loadPromises).then(function () {
                // Save our installed apps to settings
                return saveInstalledApps(_.keys(loadedApps));
            }).then(function () {
                // Extend the loadedApps onto the available apps
                _.extend(availableApps, loadedApps);
            }).otherwise(function (err) {
                errors.logError(
                    err.message || err,
                    'The app will not be loaded',
                    'Check with the app creator, or read the app documentation for more details on app requirements'
                );
            });
        });
    },
    availableApps: availableApps
};<commit_msg>Updates Ghost a function in Ghost called saveInstalledApps<commit_after>
var _           = require('lodash'),
    when        = require('when'),
    errors      = require('../errors'),
    api         = require('../api'),
    loader      = require('./loader'),
    // Holds the available apps
    availableApps = {};

function getInstalledApps() {
    return api.settings.read({context: {internal: true}, key: 'installedApps'}).then(function (response) {
        var installed = response.settings[0];

        installed.value = installed.value || '[]';

        try {
            installed = JSON.parse(installed.value);
        } catch (e) {
            return when.reject(e);
        }

        return installed;
    });
}

function saveInstalledApps(installedApps) {
    return getInstalledApps().then(function (currentInstalledApps) {
        var updatedAppsInstalled = _.uniq(installedApps.concat(currentInstalledApps));

        return api.settings.edit({settings: [{key: 'installedApps', value: updatedAppsInstalled}]}, {context: {internal: true}});    });
}

module.exports = {
    init: function () {
        var appsToLoad;

        try {
            // We have to parse the value because it's a string
            api.settings.read({context: {internal: true}, key: 'activeApps'}).then(function (response) {
                var aApps = response.settings[0];

                appsToLoad = JSON.parse(aApps.value) || [];
            });
        } catch (e) {
            errors.logError(
                'Failed to parse activeApps setting value: ' + e.message,
                'Your apps will not be loaded.',
                'Check your settings table for typos in the activeApps value. It should look like: ["app-1", "app2"] (double quotes required).'
            );
            return when.resolve();
        }

        // Grab all installed apps, install any not already installed that are in appsToLoad.
        return getInstalledApps().then(function (installedApps) {
            var loadedApps = {},
                recordLoadedApp = function (name, loadedApp) {
                    // After loading the app, add it to our hash of loaded apps
                    loadedApps[name] = loadedApp;

                    return when.resolve(loadedApp);
                },
                loadPromises = _.map(appsToLoad, function (app) {
                    // If already installed, just activate the app
                    if (_.contains(installedApps, app)) {
                        return loader.activateAppByName(app).then(function (loadedApp) {
                            return recordLoadedApp(app, loadedApp);
                        });
                    }

                    // Install, then activate the app
                    return loader.installAppByName(app).then(function () {
                        return loader.activateAppByName(app);
                    }).then(function (loadedApp) {
                        return recordLoadedApp(app, loadedApp);
                    });
                });

            return when.all(loadPromises).then(function () {
                // Save our installed apps to settings
                return saveInstalledApps(_.keys(loadedApps));
            }).then(function () {
                // Extend the loadedApps onto the available apps
                _.extend(availableApps, loadedApps);
            }).otherwise(function (err) {
                errors.logError(
                    err.message || err,
                    'The app will not be loaded',
                    'Check with the app creator, or read the app documentation for more details on app requirements'
                );
            });
        });
    },
    availableApps: availableApps
};<|endoftext|>


<commit_before>function getPlayer(players, name) {
	name = new RegExp(name.replace('-','.')+'\\s*','gi');
	for (player in players["players"]) {
		if(name.test(players["players"][player]["name"]))
			return players["players"][player];
	}
}

// Generate player info
$(document).bind( "pagebeforechange", function( e, data ) {

	// We only want to handle changePage() calls where the caller is
	// asking us to load a page by URL.
	if ( typeof data.toPage === "string" ) {
		// We are being asked to load a page by URL, but we only
		// want to handle URLs that request the data for a specific
		// category.
		var u = $.mobile.path.parseUrl( data.toPage ),
			re = /^#player-info/;

		if ( u.hash.search(re) !== -1 ) {

			// We're being asked to display the items for a specific category.
			// Call our internal method that builds the content for the category
			// on the fly based on our in-memory category data structure.
			showPlayer(u, data.options)

			// Make sure to tell changePage() we've handled this call so it doesn't
			// have to do anything.
			e.preventDefault();
		}
	}
});

// Load the data for a specific category, based on
// the URL passed in. Generate markup for the items in the
// category, inject it into an embedded page, and then make
// that page the current active page.
function showPlayer( urlObj, options )
{
	var playerName = urlObj.hash.replace( /.*player=/, "" );

	// Because of Javascript's callback structure, the rest of the
	// showPlayer function must be within this callback
	$.get('player-data.json', function(data) {
		playerInfo = $.parseJSON(data);
		playerObj = getPlayer(playerInfo, playerName);
	});

		// Get the object that represents the category we
		// are interested in. Note, that at this point we could
		// instead fire off an ajax request to fetch the data, but
		// for the purposes of this sample, it's already in memory.
/* 		player = window.playerInfo[ playerName ]; */

		// The pages we use to display our content are already in
		// the DOM. The id of the page we are going to write our
		// content into is specified in the hash before the '?'.
		//pageSelector = urlObj.hash.replace( /\?.*$/, "" );

	if ( category ) {
		// Get the page we are going to dump our content into.
		var $page = $( pageSelector ),

			// Get the header for the page.
			$header = $page.children( ":jqmData(role=header)" ),

			// Get the content area element for the page.
			$content = $page.children( ":jqmData(role=content)" ),

			// The markup we are going to inject into the content
			// area of the page.
			markup = "
" + category.description + "

",

			// The array of items for this category.
			cItems = category.items,

			// The number of items in the category.
			numItems = cItems.length;

		// Generate a list item for each item in the category
		// and add it to our markup.
		for ( var i = 0; i < numItems; i++ ) {
			markup += "
" + cItems[i].name + "
";
		}
		markup += "
";

		// Find the h1 element in our header and inject the name of
		// the category into it.
		$header.find( "h1" ).html( category.name );

		// Inject the category items markup into the content element.
		$content.html( markup );

		// Pages are lazily enhanced. We call page() on the page
		// element to make sure it is always enhanced before we
		// attempt to enhance the listview markup we just injected.
		// Subsequent calls to page() are ignored since a page/widget
		// can only be enhanced once.
		$page.page();

		// Enhance the listview we just injected.
		$content.find( ":jqmData(role=listview)" ).listview();

		// We don't want the data-url of the page we just modified
		// to be the url that shows up in the browser's location field,
		// so set the dataUrl option to the URL for the category
		// we just loaded.
		options.dataUrl = urlObj.href;

		// Now call changePage() and tell it to switch to
		// the page we just modified.
		$.mobile.changePage( $page, options );
	}
}
<commit_msg>Page changing now works<commit_after>function getPlayer(players, name) {
	name = new RegExp(name.replace('-','.')+'\\s*','gi');
	for (player in players["players"]) {
		if(name.test(players["players"][player]["name"]))
			return players["players"][player];
	}
}

// Generate player info
$(document).bind( "pagebeforechange", function( e, data ) {

	// We only want to handle changePage() calls where the caller is
	// asking us to load a page by URL.
	if ( typeof data.toPage === "string" ) {
		// We are being asked to load a page by URL, but we only
		// want to handle URLs that request the data for a specific
		// category.
		var u = $.mobile.path.parseUrl( data.toPage ),
			re = /^#player-info/;

		if ( u.hash.search(re) !== -1 ) {

			// We're being asked to display the items for a specific category.
			// Call our internal method that builds the content for the category
			// on the fly based on our in-memory category data structure.
			showPlayer(u, data.options)

			// Make sure to tell changePage() we've handled this call so it doesn't
			// have to do anything.
			e.preventDefault();
		}
	}
});

// Load the data for a specific category, based on
// the URL passed in. Generate markup for the items in the
// category, inject it into an embedded page, and then make
// that page the current active page.
function showPlayer( urlObj, options )
{
	var playerName = urlObj.hash.replace( /.*player=/, "" );

	// Because of Javascript's callback structure, the rest of the
	// showPlayer function must be within this callback
	$.get('player-data.json', function(data) {
		// Get and parse the JSON with player data
		playerInfo = $.parseJSON(data);
		playerObj = getPlayer(playerInfo, playerName);
		console.log(playerObj);

		// The pages we use to display our content are already in
		// the DOM. The id of the page we are going to write our
		// content into is specified in the hash before the '?'.
		pageSelector = urlObj.hash.replace( /\?.*$/, "" );
		console.log(pageSelector);

		// Get the page we are going to dump our content into.
		var $page = $( pageSelector );

		// Get the header for the page.
		$header = $page.children( ":jqmData(role=header)" );

		// Get the content area element for the page.
		$content = $page.children( ":jqmData(role=content)" );

		markup = "";
		markup += "
" + playerObj["bio"] + "
";

		// Inject the category items markup into the content element.
		$content.html( markup );

		// Find the h1 element in our header and inject the name of
		// the category into it.
		$header.find( "h1" ).html( playerObj["name"] );

		// Pages are lazily enhanced. We call page() on the page
		// element to make sure it is always enhanced before we
		// attempt to enhance the listview markup we just injected.
		// Subsequent calls to page() are ignored since a page/widget
		// can only be enhanced once.
		$page.page();

		// Enhance the listview we just injected.
		$content.find( ":jqmData(role=listview)" ).listview();

		// We don't want the data-url of the page we just modified
		// to be the url that shows up in the browser's location field,
		// so set the dataUrl option to the URL for the category
		// we just loaded.
		options.dataUrl = urlObj.href;

		// Now call changePage() and tell it to switch to
		// the page we just modified.
		$.mobile.changePage( $page, options );
	});

/*
	if ( category ) {

			// The markup we are going to inject into the content
			// area of the page.
			markup = "
" + category.description + "

",

			// The array of items for this category.
			cItems = category.items,

			// The number of items in the category.
			numItems = cItems.length;

		// Generate a list item for each item in the category
		// and add it to our markup.
		for ( var i = 0; i < numItems; i++ ) {
			markup += "
" + cItems[i].name + "
";
		}
		markup += "
";

	}
*/
}
<|endoftext|>


<commit_before>import React from 'react';
import styles from './EditTask.scss';
import classNames from 'classnames/bind';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { findGroup } from '../../js/findUp';
import {
  editTask
} from '../../actions/action-creators';

const css = classNames.bind(styles);

const EditTask = ({ id, title, description, editTask }) => {

  function onClick(e) {
    e.target.parentNode.parentNode.classList.toggle('js-editMode');
  };

  function onSubmit(e) {
    const { title, description } = e.currentTarget;
    const groupFrom = findGroup(e.target);
    e.preventDefault();
    e.currentTarget.parentNode.parentNode.classList.remove('js-editMode');

    editTask(id, groupFrom, title.value, description.value);
  }

  return (

      <button className={ css('editMode-btn', 'g-editMode-btn') } onClick={ onClick }>
        edit
      </button>
      <form className={ css('edit-form', 'g-edit-form') } onSubmit={ onSubmit }>
        <input className={ css('edit-title') } type="text" name="title" defaultValue={ title }></input>
        <textarea className={ css('edit-description') } name="description" defaultValue={ description } rows="3"></textarea>
        <input type="submit" value="save"></input>
      </form>

  );
}

const mapDispatchToProps = (dispatch, ownProps) => bindActionCreators({
  editTask
}, dispatch);

export { EditTask };
export default connect(null, mapDispatchToProps)(EditTask);
<commit_msg>cancel button<commit_after>import React from 'react';
import styles from './EditTask.scss';
import classNames from 'classnames/bind';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { findGroup } from '../../js/findUp';
import {
  editTask
} from '../../actions/action-creators';

const css = classNames.bind(styles);

const EditTask = ({ id, title, description, editTask }) => {

  function onEditMode(e) {
    const editBtn = e.target;
    const editModeWrapper = editBtn.parentNode;
    const taskDescription = editModeWrapper.parentNode.querySelector('.js-task-description');
    const textArea = editModeWrapper.querySelector('textarea');
    const style = window.getComputedStyle(taskDescription);

    const paddingBottom = (/\d+/).exec(style.getPropertyValue('padding-bottom')).pop();

    textArea.style.height = `${taskDescription.offsetHeight - paddingBottom + 10}px`;
    editModeWrapper.parentNode.classList.add('js-editMode');
  };

  function onCancel(e) {
    e.target.parentNode.parentNode.parentNode.parentNode.classList.remove('js-editMode');
  }

  function onSubmit(e) {
    const { title, description } = e.currentTarget;
    const groupFrom = findGroup(e.target);

    e.preventDefault();
    e.currentTarget.parentNode.parentNode.classList.remove('js-editMode');

    editTask(id, groupFrom, title.value, description.value);
  }

  return (

      <button className={ css('editMode-btn', 'g-editMode-btn') } onClick={ onEditMode }>
        edit
      </button>
      <form className={ css('edit-form', 'g-edit-form') } onSubmit={ onSubmit }>
        <input className={ css('edit-title') } type="text" name="title" defaultValue={ title }></input>
        <textarea className={ css('edit-description') } name="description" defaultValue={ description }></textarea>

          <input className={ css('save-btn', 'btn') } type="submit" name="save" value="save"></input>
          cancel

      </form>

  );
}

const mapDispatchToProps = (dispatch, ownProps) => bindActionCreators({
  editTask
}, dispatch);

export { EditTask };
export default connect(null, mapDispatchToProps)(EditTask);
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  format: require('./format'),
  listing: require('./listing'),
  add: require('./add')
};
<commit_msg>Add promise queue to queue handler<commit_after>'use strict';

// Load requirements
const pq = require('promise-queue');

// Build the module structure
module.exports = {

  // Create paused queue instance
  items: new pq(0, Infinity),

  // Total number of items added to the queue
  total: 0,

  // Current task number being run
  current: 0,

  // Load our sub-modules
  format: require('./format'),
  listing: require('./listing'),
  add: require('./add')
};
<|endoftext|>


<commit_before>//Orignal Script by : Julian Robichaux, http://www.nsftools.com
//This script has been changed/modified/added to compose it as a javscript a class, such that multiple instances of
//suggest can be used on a single page, with easily user defined properties and css settings.

Suggest = Class.create();

Suggest.prototype = {
	initialize: function()
	{
		this.name  = "";
		this.queryField = "";
		this.divName = "";
		this.ifName = "";
		this.lastVal = "";
		this.val = ""
		this.xmlHttp = null;
		this.cache = new Object();
		this.searching = false;
		this.globalDiv = "";
		this.divFormatted = false;

		this.selectionListener  = this.defaultSelectionListener;
		this.DIV_BG_COLOR = "#FFFFFF";
		this.DIV_HIGHLIGHT_COLOR = "#cccccc";
		this.listStyle = "";
		this.listItemStyle = "";
		this.listItemKey = "";
		this.listItemValue = "";
		this.listWidth = "";
		this.minCharToStartSearch=1;
		this.showKey = true;
		this.queryFieldHolds = 0;  //0 - Key, 1 = Value  , 2 - Both Key and value
	}
	,

	setQueryFieldHolds: function(value)
	{
    	this.queryFieldHolds = value;
	}
	,

	setShowKey: function(value)
	{
    	this.showKey = value;
	}
	,

	setSelectionListener: function(handler)
	{
    	this.selectionListener = handler;
	}
	,

	setMinCharToStartSearch: function(value)
	{
		this.minCharToStartSearch = value;
	}
	,

	setListWidth: function(value)
	{
		this.listWidth = value;
	}
	,

	setListStyle: function(value)
	{
		this.listStyle = value;
	}
	,

	setListItemStyle: function(value)
	{
		this.listItemStyle = value;
	}
	,

	setListItemKey: function(value)
	{
		this.listItemKey = value;
	}
	,

	setListItemValue: function(value)
	{
		this.listItemValue = value;
	}
	,

	setHighlightColor: function(value)
	{
		this.DIV_HIGHLIGHT_COLOR = value;
	}
	,

	setBackgroundColor: function(value)
	{
		this.DIV_BG_COLOR = value;
	}
	,

	/**
	The InitQueryCode function should be called by the <body onload> event, passing
	at least the queryFieldName , where:
	queryFieldName = the name of the form field we're using for lookups
	*/
	InitQueryCode: function(name, queryFieldName)
	{
		this.name = name;
		this.queryField = document.getElementsByName(queryFieldName).item(0);
		this.queryField.onblur = suggestHideDiv;
		this.queryField.onkeydown = suggestKeypressHandler;

		// for some reason, Firefox 1.0 doesn't allow us to set autocomplete to off
		// this way, so you should manually set autocomplete="off" in the input tag
		// if you can -- we'll try to set it here in case you forget
		this.queryField.autocomplete = "off";
		this.divName = name + 'div'; //"querydiv";
		this.ifName = "queryiframe";

		// add a blank value to the cache (so we don't try to do a lookup when the
		// field is empty) and start checking for changes to the input field
		this.addToCache("", new Array(), new Array());
		setTimeout("suggestMainLoop()", 100);

	}
	,

	/**
	This is a helper function that just adds results to our cache, to avoid
	repeat lookups.
	*/
	addToCache: function(queryString, resultArray1, resultArray2)
	{
		this.cache[queryString] = new Array(resultArray1, resultArray2);
	}
	,

	/**
	Get the <DIV> we're using to display the lookup results, and create the
	<DIV> if it doesn't already exist.
	*/
	getDiv: function(divID)
	{
		if (!this.globalDiv)
		{
			// if the div doesn't exist on the page already, create it
			if (!document.getElementById(divID)) {
			  	var newNode = document.createElement("div");
			  	if (this.listStyle == "")
		  			newNode.className = "_listStyle";
				else
					newNode.className = this.listStyle;

				newNode.setAttribute("id", divID);
			  	document.body.appendChild(newNode);
			}

			// set the globalDiv reference
			this.globalDiv = document.getElementById(divID);

			// figure out where the top corner of the div should be, based on the
			// bottom left corner of the input field
			var x = this.queryField.offsetLeft;
			var y = this.queryField.offsetTop + this.queryField.offsetHeight;
			var parent = this.queryField;

			while (parent.offsetParent) {
				parent = parent.offsetParent;
				x += parent.offsetLeft;
				y += parent.offsetTop;
			}

			// add some formatting to the div, if we haven't already
			if (!this.divFormatted)
			{
				this.globalDiv.style.backgroundColor = this.DIV_BG_COLOR;

				if (this.listStyle == "")
				{
					this.globalDiv.style.fontFamily = "Verdana, Geneva, Arial, Helvetica, sans-serif";
					this.globalDiv.style.fontSize = "90%";
					this.globalDiv.style.padding = "4px";
					this.globalDiv.style.border = "1px solid black";
				}

				this.globalDiv.style.position = "absolute";
				this.globalDiv.style.left = x + "px";
				this.globalDiv.style.top = y + "px";
				this.globalDiv.style.visibility = "hidden";
				this.globalDiv.style.zIndex = 10000;
				if (this.listWidth != "") this.globalDiv.style.width = this.listWidth;

				this.divFormatted = true;
			}
		}
		return this.globalDiv;
	}
	,

	/**
	This is the function that should be returned by the XMLHTTP call. It will
	format and display the lookup results.
	*/
	showQueryDiv: function(queryString, resultArray1, resultArray2)
	{
		var div = this.getDiv(this.divName);

		// remove any results that are already there
		while (div.childNodes.length > 0)
			div.removeChild(div.childNodes[0]);

		// add an entry for each of the results in the resultArray
		for (var i = 0; i < resultArray1.length; i++)
		{
			// each result will be contained within its own div
			var result = document.createElement("div");
			if (this.listItemStyle != "")
			{
				result.className = this.listItemStyle;
			}
			else
			{
				result.style.cursor = "pointer";
				result.style.borderBottom = "2px solid #777777";
				result.style.padding = "3px 0px 3px 0px"
			}

			this._unhighlightResult(result);
			result.onmousedown = this.selectResult;
			result.onmouseover = this.highlightResult;
			result.onmouseout = this.unhighlightResult;

			var result1 = document.createElement("span");
			if (this.listItemKey == "")
				result1.className = "_listItemKey";
			else
				result1.className = this.listItemKey;

			if (this.listItemKey == "")
			{
				result1.style.textAlign = "left";
				result1.style.fontWeight = "bold";
			}
			result1.innerHTML = resultArray1[i];

			var result2 = document.createElement("span");

			if (this.listItemValue == "")
				result2.className = "_listItemValue";
			else
				result2.className = this.listItemValue;

			if (this.listItemValue == "")
			{
				result2.style.textAlign = "right";
				result2.style.paddingLeft = "20px";
			}
			result2.innerHTML = resultArray2[i];

			var result3 = document.createElement("span");
			result3.className = resultArray1[i] + ";" + resultArray2[i];

			result.appendChild(result3);
			if (this.showKey) result.appendChild(result1);
			result.appendChild(result2);
			div.appendChild(result);
		}

		// if this resultset isn't already in our cache, add it
		var isCached = this.cache[queryString];
		if (!isCached)
			this.addToCache(queryString, resultArray1, resultArray2);

		// display the div if we had at least one result
		this.showDiv(resultArray1.length > 0);
	}
	,

	/**
	This is called whenever the user clicks one of the lookup results.
	It puts the value of the result in the queryField and hides the
	lookup div.
	*/
	selectResult: function()
	{
		selectedSuggestObject._selectResult(this);
	}
	,

	/** This actually fills the field with the selected result and hides the div */
	_selectResult: function(item)
	{
		var spans = item.getElementsByTagName("span");
		if (spans) {

			var data = spans[0].className.split(";");

			if (this.queryFieldHolds == 0) {
				//key
				this.queryField.value = data[0];
			} else if (this.queryFieldHolds == 1) {
				//value
				this.queryField.value = data[1];
			} else if (this.queryFieldHolds == 2) {
				//both key and value
				this.queryField.value = data[0] + " - " + data[1];
			}

			this.lastVal = val = escape(this.queryField.value);
			this.searching = false;
			suggestMainLoop();
			this.queryField.focus();
			this.showDiv(false);

			var _retData = new Array();
			_retData.KEY = data[0];
			_retData.VALUE = data[1];
			this.selectionListener(_retData);

			return;
		}
	}
	,

	/**
	This is called when a user mouses over a lookup result
	*/
	highlightResult: function()
	{
		selectedSuggestObject._highlightResult(this);
	}
	,

	/** This actually highlights the selected result */
	_highlightResult: function(item)
	{
	  item.style.backgroundColor = selectedSuggestObject.DIV_HIGHLIGHT_COLOR;
	}
	,

	/**
	This is called when a user mouses away from a lookup result
	*/
	unhighlightResult: function()
	{
		selectedSuggestObject._unhighlightResult(this);
	}
	,

	/** This actually unhighlights the selected result */
	_unhighlightResult: function(item)
	{
	  item.style.backgroundColor = selectedSuggestObject.DIV_BG_COLOR;
	}
	,

	/**
	This either shows or hides the lookup div, depending on the value of
	the "show" parameter.
	*/
	showDiv: function(show)
	{
		var div = this.getDiv(this.divName);
		if (show)
			div.style.visibility = "visible";
		else
			div.style.visibility = "hidden";

		this.adjustiFrame();
	}
	,

	/**
	Use an "iFrame shim" to deal with problems where the lookup div shows up behind
	selection list elements, if they're below the queryField. The problem and solution are
	described at:

	http://dotnetjunkies.com/WebLog/jking/archive/2003/07/21/488.aspx
	http://dotnetjunkies.com/WebLog/jking/archive/2003/10/30/2975.aspx
	*/
	adjustiFrame: function()
	{
		if (!document.getElementById(this.ifName))
		{
			var newNode = document.createElement("iFrame");
			newNode.setAttribute("id", this.ifName);
			newNode.setAttribute("src", "javascript:false;");
			newNode.setAttribute("scrolling", "no");
			newNode.setAttribute("frameborder", "0");
			newNode.setAttribute("width", "0");
			document.body.appendChild(newNode);
		}

		iFrameDiv = document.getElementById(this.ifName);
		var div = this.getDiv(this.divName);

		try {
			iFrameDiv.style.position = "absolute";
			if (this.listWidth != "")
				this.globalDiv.style.width = this.listWidth;
			else
				iFrameDiv.style.width = div.offsetWidth;
			//alert(div.offsetWidth);
			iFrameDiv.style.height = div.offsetHeight;
			iFrameDiv.style.top = div.style.top;
			iFrameDiv.style.left = div.style.left;
			iFrameDiv.style.zIndex = div.style.zIndex - 1;
			iFrameDiv.style.visibility = div.style.visibility;
		} catch(e) {
		}
	}
	,

	/**
	Get the number of the result that's currently selected/highlighted
	(the first result is 0, the second is 1, etc.)
	*/
	getSelectedSpanNum: function(div)
	{
		var count = -1;
		var spans = div.getElementsByTagName("div");
		if (spans)
		{
			for (var i = 0; i < spans.length; i++)
			{
				count++;
				if (spans[i].style.backgroundColor != div.style.backgroundColor)
					return count;
			}
		}
		return -1;
	}
	,

	/**
	Select/highlight the result at the given position
	*/
	setSelectedSpan: function(div, spanNum)
	{
		var count = -1;
		var thisSpan;
		var spans = div.getElementsByTagName("div");
		if (spans)
		{
			for (var i = 0; i < spans.length; i++)
			{
				if (++count == spanNum)
				{
					this._highlightResult(spans[i]);
					thisSpan = spans[i];
				} else {
					this._unhighlightResult(spans[i]);
				}
			}
		}
		return thisSpan;
	}
	,

	defaultSelectionListener: function(result)
	{
		//do nothing -- customer listener required
	}
}

var selectedSuggestObject = null;
function onSuggestFieldFocus(object)
{
	selectedSuggestObject = object;
}

/**
We originally used showDiv as the function that was called by the onBlur
event of the field, but it turns out that Firefox will pass an event as the first
parameter of the function, which would cause the div to always be visible.
So onBlur now calls suggestHideDiv instead.
*/
function suggestHideDiv()
{
	selectedSuggestObject.searching = false;
	selectedSuggestObject.showDiv(false);
}

/**
This is the key handler function, for when a user presses the up arrow,
down arrow, tab key, or enter key from the input field.
*/
function suggestKeypressHandler(evt)
{
	// don't do anything if the div is hidden
	var div = selectedSuggestObject.getDiv(selectedSuggestObject.divName);
	if (div.style.visibility == "hidden")
		return true;

	// make sure we have a valid event variable
	if(!evt && window.event)
	{
		evt = window.event;
	}

	var key = evt.keyCode;

	// if this key isn't one of the ones we care about, just return
	var KEYUP = 38;
	var KEYDOWN = 40;
	var KEYENTER = 13;
	var KEYTAB = 9;

	if ((key != KEYUP) && (key != KEYDOWN) && (key != KEYENTER) && (key != KEYTAB))
		return true;

	// get the span that's currently selected, and perform an appropriate action
	var selNum = selectedSuggestObject.getSelectedSpanNum(div);
	var selSpan = selectedSuggestObject.setSelectedSpan(div, selNum);

	if ((key == KEYENTER) || (key == KEYTAB))
	{
		if (selSpan)
		{
			selectedSuggestObject._selectResult(selSpan);
		}
		evt.cancelBubble=true;
		return false;
	} else {
	if (key == KEYUP)
		selSpan = selectedSuggestObject.setSelectedSpan(div, selNum - 1);
	if (key == KEYDOWN)
		selSpan = selectedSuggestObject.setSelectedSpan(div, selNum + 1);
	if (selSpan)
		selectedSuggestObject._highlightResult(selSpan);
	}

	selectedSuggestObject.showDiv(true);
	return true;
}

/**
This is the function that monitors the queryField, and calls the lookup
functions when the queryField value changes.
*/
function suggestMainLoop()
{
	val = escape(selectedSuggestObject.queryField.value);

 	if (val.length >= selectedSuggestObject.minCharToStartSearch)
	{
		// if the field value has changed and we're not currently waiting for
		// a lookup result to be returned, do a lookup (or use the cache, if
		// we can)
		if (selectedSuggestObject.lastVal != val && selectedSuggestObject.searching == false)
		{
			var cacheResult = selectedSuggestObject.cache[val];
			if (cacheResult)
			  selectedSuggestObject.showQueryDiv(val, cacheResult[0], cacheResult[1]);
			else
			  getData(val);
			selectedSuggestObject.lastVal = val;
		}
	}
	setTimeout("suggestMainLoop()", 100);
	return true;
}<commit_msg>Delete suggest.js<commit_after><|endoftext|>