App-Alice

 view release on metacpan or  search on metacpan

share/static/alice.js  view on Meta::CPAN

	}
};

function str_repeat(i, m) {
	for (var o = []; m > 0; o[--m] = i);
	return o.join('');
}

function sprintf() {
	var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
	while (f) {
		if (m = /^[^\x25]+/.exec(f)) {
			o.push(m[0]);
		}
		else if (m = /^\x25{2}/.exec(f)) {
			o.push('%');
		}
		else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
			if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
				throw('Too few arguments.');
			}
			if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
				throw('Expecting number but found ' + typeof(a));
			}
			switch (m[7]) {
				case 'b': a = a.toString(2); break;
				case 'c': a = String.fromCharCode(a); break;
				case 'd': a = parseInt(a); break;
				case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
				case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
				case 'o': a = a.toString(8); break;
				case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
				case 'u': a = Math.abs(a); break;
				case 'x': a = a.toString(16); break;
				case 'X': a = a.toString(16).toUpperCase(); break;
			}
			a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
			c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
			x = m[5] - String(a).length - s.length;
			p = m[5] ? str_repeat(c, x) : '';
			o.push(s + (m[4] ? a + p : p + a));
		}
		else {
			throw('Huh ?!');
		}
		f = f.substring(m[0].length);
	}
	return o.join('');
}
/*  WysiHat - WYSIWYG JavaScript framework, version 0.2.1
 *  (c) 2008-2010 Joshua Peek
 *
 *  WysiHat is freely distributable under the terms of an MIT-style license.
 *--------------------------------------------------------------------------*/


var WysiHat = {};

WysiHat.Editor = {
  attach: function(textarea) {
    var editArea;

    textarea = $(textarea);

    var id = textarea.id + '_editor';
    if (editArea = $(id)) return editArea;

    editArea = new Element('div', {
      'id': id,
      'class': 'editor',
      'contentEditable': 'true'
    });

    editArea.update(WysiHat.Formatting.getBrowserMarkupFrom(textarea.value));

    Object.extend(editArea, WysiHat.Commands);

    textarea.insert({before: editArea});
    textarea.hide();


    return editArea;
  }
};
WysiHat.BrowserFeatures = (function() {
  function createTmpIframe(callback) {
    var frame, frameDocument;

    frame = new Element('iframe');
    frame.setStyle({
      position: 'absolute',
      left: '-1000px'
    });

    frame.onFrameLoaded(function() {
      if (typeof frame.contentDocument !== 'undefined') {
        frameDocument = frame.contentDocument;
      } else if (typeof frame.contentWindow !== 'undefined' && typeof frame.contentWindow.document !== 'undefined') {
        frameDocument = frame.contentWindow.document;
      }

      frameDocument.designMode = 'on';

      callback(frameDocument);

      frame.remove();
    });

    $(document.body).insert(frame);
  }

  var features = {};

  function detectParagraphType(document) {
    document.body.innerHTML = '';
    document.execCommand('insertparagraph', false, null);

    var tagName;
    element = document.body.childNodes[0];
    if (element && element.tagName)
      tagName = element.tagName.toLowerCase();

    if (tagName == 'div')
      features.paragraphType = "div";
    else if (document.body.innerHTML == "<p><br></p>")
      features.paragraphType = "br";
    else
      features.paragraphType = "p";
  }

  function detectIndentType(document) {
    document.body.innerHTML = 'tab';
    document.execCommand('indent', false, null);

    var tagName;
    element = document.body.childNodes[0];
    if (element && element.tagName)
      tagName = element.tagName.toLowerCase();
    features.indentInsertsBlockquote = (tagName == 'blockquote');
  }

  features.run = function run() {

share/static/alice.js  view on Meta::CPAN

if (Prototype.Browser.IE) {
  Object.extend(Selection.prototype, (function() {
    function getNode() {
      var range = this._document.selection.createRange();
      return $(range.parentElement());
    }

    function selectNode(element) {
      var range = this._document.body.createTextRange();
      range.moveToElementText(element);
      range.select();
    }

    return {
      getNode:    getNode,
      selectNode: selectNode
    }
  })());
} else {
  if (typeof Selection == 'undefined') {
    var Selection = {}
    Selection.prototype = window.getSelection().__proto__;
  }

  Object.extend(Selection.prototype, (function() {
    function getNode() {
      if (this.rangeCount > 0)
        return this.getRangeAt(0).getNode();
      else
        return null;
    }

    function selectNode(element) {
      var range = document.createRange();
      range.selectNode(element);
      this.removeAllRanges();
      this.addRange(range);
    }

    return {
      getNode:    getNode,
      selectNode: selectNode
    }
  })());
}
document.on("dom:loaded", function() {
  function fieldChangeHandler(event, element) {
    var value;

    if (element.contentEditable == 'true')
      value = element.innerHTML;
    else if (element.getValue)
      value = element.getValue();

    if (value && element.previousValue != value) {
      element.fire("field:change");
      element.previousValue = value;
    }
  }

  $(document.body).on("keyup", 'input,textarea,*[contenteditable=""],*[contenteditable=true]', fieldChangeHandler);
});

WysiHat.Commands = (function(window) {
  function boldSelection() {
    this.execCommand('bold', false, null);
  }

  function boldSelected() {
    return this.queryCommandState('bold');
  }

  function underlineSelection() {
    this.execCommand('underline', false, null);
  }

  function underlineSelected() {
    return this.queryCommandState('underline');
  }

  function italicSelection() {
    this.execCommand('italic', false, null);
  }

  function italicSelected() {
    return this.queryCommandState('italic');
  }

  function strikethroughSelection() {
    this.execCommand('strikethrough', false, null);
  }

  function indentSelection() {
    if (Prototype.Browser.Gecko) {
      var selection, range, node, blockquote;

      selection = window.getSelection();
      range     = selection.getRangeAt(0);
      node      = selection.getNode();

      if (range.collapsed) {
        range = document.createRange();
        range.selectNodeContents(node);
        selection.removeAllRanges();
        selection.addRange(range);
      }

      blockquote = new Element('blockquote');
      range = selection.getRangeAt(0);
      range.surroundContents(blockquote);
    } else {
      this.execCommand('indent', false, null);
    }
  }

  function outdentSelection() {
    this.execCommand('outdent', false, null);
  }

  function toggleIndentation() {
    if (this.indentSelected()) {

share/static/alice.js  view on Meta::CPAN


  function backgroundColorSelection(color) {
    if(Prototype.Browser.Gecko) {
      this.execCommand('hilitecolor', false, color);
    } else {
      this.execCommand('backcolor', false, color);
    }
  }

  function alignSelection(alignment) {
    this.execCommand('justify' + alignment);
  }

  function alignSelected() {
    var node = window.getSelection().getNode();
    return Element.getStyle(node, 'textAlign');
  }

  function linkSelection(url) {
    this.execCommand('createLink', false, url);
  }

  function unlinkSelection() {
    var node = window.getSelection().getNode();
    if (this.linkSelected())
      window.getSelection().selectNode(node);

    this.execCommand('unlink', false, null);
  }

  function linkSelected() {
    var node = window.getSelection().getNode();
    return node ? node.tagName.toUpperCase() == 'A' : false;
  }

  function formatblockSelection(element){
    this.execCommand('formatblock', false, element);
  }

  function toggleOrderedList() {
    var selection, node;

    selection = window.getSelection();
    node      = selection.getNode();

    if (this.orderedListSelected() && !node.match("ol li:last-child, ol li:last-child *")) {
      selection.selectNode(node.up("ol"));
    } else if (this.unorderedListSelected()) {
      selection.selectNode(node.up("ul"));
    }

    this.execCommand('insertorderedlist', false, null);
  }

  function insertOrderedList() {
    this.toggleOrderedList();
  }

  function orderedListSelected() {
    var element = window.getSelection().getNode();
    if (element) return element.match('*[contenteditable=""] ol, *[contenteditable=true] ol, *[contenteditable=""] ol *, *[contenteditable=true] ol *');
    return false;
  }

  function toggleUnorderedList() {
    var selection, node;

    selection = window.getSelection();
    node      = selection.getNode();

    if (this.unorderedListSelected() && !node.match("ul li:last-child, ul li:last-child *")) {
      selection.selectNode(node.up("ul"));
    } else if (this.orderedListSelected()) {
      selection.selectNode(node.up("ol"));
    }

    this.execCommand('insertunorderedlist', false, null);
  }

  function insertUnorderedList() {
    this.toggleUnorderedList();
  }

  function unorderedListSelected() {
    var element = window.getSelection().getNode();
    if (element) return element.match('*[contenteditable=""] ul, *[contenteditable=true] ul, *[contenteditable=""] ul *, *[contenteditable=true] ul *');
    return false;
  }

  function insertImage(url) {
    this.execCommand('insertImage', false, url);
  }

  function insertHTML(html) {
    if (Prototype.Browser.IE) {
      var range = window.document.selection.createRange();
      range.pasteHTML(html);
      range.collapse(false);
      range.select();
    } else {
      this.execCommand('insertHTML', false, html);
    }
  }

  function execCommand(command, ui, value) {
    var handler = this.commands.get(command);
    if (handler) {
      handler.bind(this)(value);
    } else {
      try {
        window.document.execCommand(command, ui, value);
      } catch(e) { return null; }
    }

    document.activeElement.fire("field:change");
  }

  function queryCommandState(state) {
    var handler = this.queryCommands.get(state);
    if (handler) {
      return handler.bind(this)();
    } else {
      try {
        return window.document.queryCommandState(state);
      } catch(e) { return null; }
    }
  }

  function getSelectedStyles() {
    var styles = $H({});
    var editor = this;
    editor.styleSelectors.each(function(style){
      var node = editor.selection.getNode();
      styles.set(style.first(), Element.getStyle(node, style.last()));
    });
    return styles;
  }

  return {
     boldSelection:            boldSelection,
     boldSelected:             boldSelected,
     underlineSelection:       underlineSelection,
     underlineSelected:        underlineSelected,
     italicSelection:          italicSelection,
     italicSelected:           italicSelected,
     strikethroughSelection:   strikethroughSelection,
     indentSelection:          indentSelection,
     outdentSelection:         outdentSelection,
     toggleIndentation:        toggleIndentation,
     indentSelected:           indentSelected,
     fontSelection:            fontSelection,
     fontSizeSelection:        fontSizeSelection,
     colorSelection:           colorSelection,
     backgroundColorSelection: backgroundColorSelection,
     alignSelection:           alignSelection,
     alignSelected:            alignSelected,
     linkSelection:            linkSelection,
     unlinkSelection:          unlinkSelection,
     linkSelected:             linkSelected,
     formatblockSelection:     formatblockSelection,
     toggleOrderedList:        toggleOrderedList,
     insertOrderedList:        insertOrderedList,
     orderedListSelected:      orderedListSelected,
     toggleUnorderedList:      toggleUnorderedList,
     insertUnorderedList:      insertUnorderedList,
     unorderedListSelected:    unorderedListSelected,
     insertImage:              insertImage,
     insertHTML:               insertHTML,
     execCommand:              execCommand,
     queryCommandState:        queryCommandState,
     getSelectedStyles:        getSelectedStyles,

    commands: $H({}),

    queryCommands: $H({
      link:          linkSelected,
      orderedlist:   orderedListSelected,
      unorderedlist: unorderedListSelected
    }),

    styleSelectors: $H({
      fontname:    'fontFamily',
      fontsize:    'fontSize',
      forecolor:   'color',
      hilitecolor: 'backgroundColor',
      backcolor:   'backgroundColor'
    })
  };
})(window);


if (Prototype.Browser.IE) {
  Object.extend(Selection.prototype, (function() {

share/static/alice.js  view on Meta::CPAN

        var element = node.cloneNode(false);

        if (tagName == "span") {
          if ($(node).getStyle("fontWeight") == "bold") {
            element = new Element("strong");

          } else if ($(node).getStyle("fontStyle") == "italic") {
            element = new Element("em");
          }
        }

        accumulate(element);
        lineContainer = element;
      }

      function accumulate(node) {
        if (mode != EXPECTING_LIST_ITEM) {
          if (!line) line = lineContainer = createLine();
          previousAccumulation = node;
          lineContainer.appendChild(node);
        }
      }

      function getPreviouslyAccumulatedTagName() {
        if (previousAccumulation && previousAccumulation.nodeType == Node.ELEMENT_NODE) {
          return previousAccumulation.tagName.toLowerCase();
        }
      }

      function flush() {
        if (line && line.childNodes.length) {
          container.appendChild(line);
          line = lineContainer = null;
        }
      }

      function createLine() {
        if (mode == ACCUMULATING_LINE) {
          return new Element("div");
        } else if (mode == ACCUMULATING_LIST_ITEM) {
          return new Element("li");
        }
      }

      function insertList(tagName) {
        var list = new Element(tagName);
        result.appendChild(list);
        return list;
      }

      result = container = new Element("div");
      walk(element.childNodes);
      flush();
      return result.innerHTML;
    }
  };
})();


WysiHat.Toolbar = Class.create((function() {
  function initialize(editor) {
    this.editor = editor;
    this.element = this.createToolbarElement();
  }

  function createToolbarElement() {
    var toolbar = new Element('div', { 'class': 'editor_toolbar' });
    this.editor.insert({before: toolbar});
    return toolbar;
  }

  function addButtonSet(set) {
    $A(set).each(function(button){
      this.addButton(button);
    }.bind(this));
  }

  function addButton(options, handler) {
    options = $H(options);

    if (!options.get('name'))
      options.set('name', options.get('label').toLowerCase());
    var name = options.get('name');

    var button = this.createButtonElement(this.element, options);

    var handler = this.buttonHandler(name, options);
    this.observeButtonClick(button, handler);

    var handler = this.buttonStateHandler(name, options);
    this.observeStateChanges(button, name, handler);
  }

  function createButtonElement(toolbar, options) {
    var button = new Element('a', {
      'class': 'button', 'href': '#'
    });
    button.update('<span>' + options.get('label') + '</span>');
    button.addClassName(options.get('name'));

    toolbar.appendChild(button);

    return button;
  }

  function buttonHandler(name, options) {
    if (options.handler)
      return options.handler;
    else if (options.get('handler'))
      return options.get('handler');
    else
      return function(editor) { editor.execCommand(name); };
  }

  function observeButtonClick(element, handler) {
    element.on('click', function(event) {
      handler(this.editor);
      event.stop();
    }.bind(this));
  }

  function buttonStateHandler(name, options) {
    if (options.query)
      return options.query;
    else if (options.get('query'))
      return options.get('query');
    else
      return function(editor) { return editor.queryCommandState(name); };
  }

  function observeStateChanges(element, name, handler) {
    var previousState;
    this.editor.on("selection:change", function(event) {
      var state = handler(this.editor);
      if (state != previousState) {
        previousState = state;
        this.updateButtonState(element, name, state);
      }
    }.bind(this));
  }

  function updateButtonState(element, name, state) {
    if (state)
      element.addClassName('selected');
    else
      element.removeClassName('selected');
  }

  return {
    initialize:           initialize,
    createToolbarElement: createToolbarElement,
    addButtonSet:         addButtonSet,
    addButton:            addButton,
    createButtonElement:  createButtonElement,
    buttonHandler:        buttonHandler,
    observeButtonClick:   observeButtonClick,
    buttonStateHandler:   buttonStateHandler,
    observeStateChanges:  observeStateChanges,
    updateButtonState:    updateButtonState
  };
})());

WysiHat.Toolbar.ButtonSets = {};

WysiHat.Toolbar.ButtonSets.Basic = $A([
  { label: "Bold" },
  { label: "Underline" },
  { label: "Italic" }
]);

var Alice = { };

Object.extend(Alice, {
  uncacheGravatar: function(content) {
    if (!this.timestamp) {
      var date = new Date();
      this.timestamp = date.getTime();
    }
    return content.replace(
      /(src=".*?gravatar.com\/avatar\/[^?]*\?)/gi,
      "$1time=" + this.timestamp + "&"
    );
  },

  epochToLocal: function(epoch, format) {
    var date = new Date(parseInt(epoch) * 1000);
    if (!date) return epoch;

    var hours = date.getHours();

    if (format == "12") {
      var ap;
      if (hours > 12) {
        hours -= 12;

share/static/alice.js  view on Meta::CPAN

      this.nicks = message.nicks;

    if (this.element.hasClassName('active'))
      this.scrollToBottom();
    else if (this.title != "info") {
      if (message.event == "say") {
        this.tab.addClassName("unread");
        this.tabOverflowButton.addClassName("unread");
        if (this.isTabWrapped()) this.application.highlightChannelSelect();
      }
      if (message.highlight) {
        this.tab.addClassName("highlight");
      }
    }

    var messages = this.messages.down('ul').childElements();
    if (messages.length > this.messageLimit) messages.first().remove();

    li.select("span.timestamp").each(function(elem) {
      elem.innerHTML = Alice.epochToLocal(elem.innerHTML.strip(), this.application.options.timeformat);
      elem.style.opacity = 1;
    }.bind(this));

    this.element.redraw();
  },

  scrollToBottom: function(force) {
    var bottom, height;

    if (!force) {
      var lastmsg = this.messages.down('ul.messages > li:last-child');
      if (!lastmsg) return;
      var msgheight = lastmsg.offsetHeight;
      bottom = this.messages.scrollTop + this.messages.offsetHeight;
      height = this.messages.scrollHeight;
    }

    if (force || bottom + msgheight + 100 >= height) {
      this.messages.scrollTop = this.messages.scrollHeight;
      this.element.redraw();
    }
  },

  getNicknames: function() {
    return this.nicks;
  }
});

Alice.Toolbar = Class.create(WysiHat.Toolbar, {
  createButtonElement: function(toolbar, options) {
    var button = Element('button');
    button.update(options.get('label'));
    button.addClassName(options.get('name'));
    toolbar.appendChild(button);

    return button;
  },
  observeButtonClick: function(element, handler) {
    element.on('click', function(event) {

      handler(this.editor, element, this);

      this.editor.fire("selection:change");

      event.stop();
    }.bind(this));
  }
});

Alice.Toolbar.ButtonSet = WysiHat.Toolbar.ButtonSets.Basic.concat(
  [
    {
      label: "Colors",
      handler: function (editor, button, toolbar) {
        var cb = function (color, fg) {
          fg ? editor.colorSelection(color) : editor.backgroundColorSelection(color)
        };
        if (toolbar.picker) {
          toolbar.picker.remove();
          toolbar.picker = undefined;
        } else {
          toolbar.picker = new Alice.Colorpicker(button, cb);
        }
      }
    },
    {
      label: "&raquo;",
      handler: function (editor, button, toolbar) {
        button.up("div.editor_toolbar").removeClassName("visible");
        if (toolbar.picker) {
          toolbar.picker.remove();
          toolbar.picker = undefined;
        }
      }
    }
  ]
);

Alice.Colorpicker = Class.create({
  initialize: function(button, callback) {
    var elem = new Element("div").addClassName("color_picker");

    var toggle = new Element("div").addClassName("toggle");
    var blank = new Element("span").addClassName("blank").addClassName("color");
    blank.setStyle({"background-color": "none"});
    blank.insert("&#8416;");
    toggle.insert('<span id="fg" class="active">fg</span><span id="bg">bg</span>');
    toggle.insert(blank);
    elem.insert(toggle);

    var colorcontainer = new Element("div").addClassName("colors");
    this.colors().each(function(color) {
      var box = new Element("span").addClassName("color");
      box.setStyle({"background-color": color});
      colorcontainer.insert(box);
    });
    elem.insert(colorcontainer);

    button.up('.window').insert(elem);
    elem.observe("mousedown", this.clicked.bind(this));

    this.elem = elem;
    this.cb = callback;
    this.fg = true;
  },

  clicked: function(e) {
    e.stop();

    var box = e.findElement("span.color");
    if (box) {
      var color = box.getStyle("background-color");
      if (color) this.cb(color, this.fg);
      return;
    }

    if (e.findElement("span#fg")) {
      this.elem.down("#bg").removeClassName("active");
      this.elem.down("#fg").addClassName("active");
      this.fg = true;
      return;
    }

    if (e.findElement("span#bg")) {
      this.elem.down("#fg").removeClassName("active");
      this.elem.down("#bg").addClassName("active");
      this.fg = false;
      return;
    }
  },

  remove: function() {
    this.elem.remove();
  },

  colors: function() {
    return ["#fff", "#000", "#008", "#080", "#f00", "#800", "#808", "#f80",
            "#ff0", "#0f0", "#088", "#0ff", "#00f", "#f0f", "#888", "#ccc"];
  }
});
Alice.Input = Class.create({
  initialize: function(win, element) {

    this.window = win;
    this.application = this.window.application;
    this.textarea = $(element);
    this.disabled = false;

    if (this.canContentEditable()) {
      this.editor = WysiHat.Editor.attach(this.textarea);
      this.element = this.editor;
      this.toolbar = new Alice.Toolbar(this.element)
      this.toolbar.addButtonSet(Alice.Toolbar.ButtonSet);
      this.toolbar.element.observe("click", function(e) {
        if (this.toolbar.element.hasClassName("visible")) return;
        this.toolbar.element.addClassName("visible");
        this.focus();
      }.bind(this));
      var input = new Element("input", {type: "hidden", name: "html", value: 1});
      this.textarea.form.appendChild(input);

      document.observe("mousedown", function(e) {
        if (!e.findElement(".editor")) this.uncancelNextFocus();
      }.bind(this));

      this.editor.observe("keydown", function(){this.cancelNextFocus()}.bind(this));
      this.editor.observe("keyup", this.updateRange.bind(this));
      this.editor.observe("mouseup", this.updateRange.bind(this));
      this.editor.observe("paste", this.pasteHandler.bind(this));

      this.toolbar.element.on("mouseup","button",function(){
        this.cancelNextFocus();
      }.bind(this));
    } else {
      this.element = this.textarea;
    }

    this.history = [];
    this.index = -1;
    this.buffer = "";
    this.completion = false;
    this.focused = false;

    this.element.observe("keypress", this.onKeyPress.bind(this));
    this.element.observe("blur", this.onBlur.bind(this));
    this.element.observe("keydown", this.resize.bind(this));
    this.element.observe("cut", this.resize.bind(this));
    this.element.observe("paste", this.resize.bind(this));
    this.element.observe("change", this.resize.bind(this));

  },

  setValue: function(value) {
    this.editor ? this.editor.update(value) : this.textarea.setValue(value);
  },

  getValue: function() {
    if (this.editor) {
      return this.editor.innerHTML;
    }
    return this.textarea.getValue();
  },

  onKeyPress: function(event) {
    if (event.keyCode != Event.KEY_TAB) {
      this.completion = false;
    }
  },

  uncancelNextFocus: function() {
    this.skipThisFocus = false;
  },

  cancelNextFocus: function() {
    this.skipThisFocus = true;
  },

  focus: function(force) {
    if (this.disabled) return;

    if (!force) {
      if (this.focused) return;

      if (this.skipThisFocus) {
        this.skipThisFocus = false;
        return;
      }
    }

    this.focused = true;

    if (this.editor) {
      var selection = window.getSelection();
      selection.removeAllRanges();
      if (this.range) {
        selection.addRange(this.range);
      } else {
        var text = document.createTextNode("");
        this.editor.appendChild(text);
        selection.selectNode(text);
        this.range = selection.getRangeAt(0);
      }
      this.editor.focus();
    } else {
      this.textarea.focus();
    }
  },

  onBlur: function(e) {
    this.focused = false;
  },

  previousCommand: function() {
    if (this.index-- == -1) {
      this.index = this.history.length - 1;
      this.stash();
    }

    this.update();
  },

  nextCommand: function() {
    if (this.index++ == -1) {
      this.stash();
    } else if (this.index == this.history.length) {
      this.index = -1;
    }

    this.update();
  },

  newLine: function() {
    console.log("newLine");
  },

  send: function() {
    this.application.connection.sendMessage(this.textarea.form);
    this.history.push(this.getValue());
    this.setValue("");
    if (this.editor) this.editor.update();
    this.index = -1;
    this.stash();
    this.update();
    this.focus(1);
  },

  completeNickname: function() {
    if (this.disabled) return;
    if (!this.completion) {
      this.completion = new Alice.Completion(this.window.getNicknames());
    }

    this.completion.next();
  },

  stopCompletion: function() {
    if (this.completion) {
      this.completion.restore();
      this.completion = false;
    }
  },

  stash: function() {
    this.buffer = this.getValue();
  },

  update: function() {
    this.setValue(this.getCommand(this.index));
  },

  getCommand: function(index) {
    if (index == -1) {
      return this.buffer;
    } else {
      return this.history[index];
    }
  },

  resize: function() {
    if (this.editor) {
      this.textarea.setValue(this.editor.innerHTML);
    }
    (function() {
      if (!this.window.active) return;
      var height = this.getContentHeight();
      if (height == 0) {
        this.element.setStyle({ height: null, top: 0 });
      } else if (height <= 150) {
        this.element.setStyle({ height: height + "px", top: "-1px" });
      }
    }).bind(this).defer();
  },

  getContentHeight: function() {
    var element = new Element("div").setStyle({
      position:   "absolute",
      visibility: "hidden",
      left:       "-" + this.element.getWidth() + "px",
      width:      this.element.getWidth() - 7 + "px",
      fontFamily: this.element.getStyle("fontFamily"),
      fontSize:   this.element.getStyle("fontSize"),
      lineHeight: this.element.getStyle("lineHeight"),
      whiteSpace: "pre-wrap",
      wordWrap:   "break-word"
    });

    if (this.editor) element.addClassName("editor");

    var value = this.getValue();
    element.update(value.replace(/\n$/, "\n\n").replace("\n", "<br>"));
    $(document.body).insert(element);

    var height = element.getHeight();
    element.remove();
    return height;
  },

  canContentEditable: function() {
    var element = new Element("div", {contentEditable: "true"});
    return element.contentEditable != null && ! this.application.isMobile;
  },

  updateRange: function (e) {
    var selection = window.getSelection();
    if (selection.rangeCount > 0) {
      var range = selection.getRangeAt(0);
      this.range = range;
    }
  },

  pasteHandler: function(e) {
    var url = e.clipboardData.getData("URL");
    if (url) {
      e.preventDefault();
      this.editor.insertHTML(url);
      this.updateRange();
      return;
    }

    var text = e.clipboardData.getData("Text");
    if (text) {
      e.preventDefault();
      text = text.escapeHTML().replace(/\n+/g, "<br>");
      this.editor.insertHTML(text);
      this.updateRange();
      return;
    }
  }
});
Alice.Keyboard = Class.create({
  initialize: function(application) {
    this.application = application;
    this.isMac = navigator.platform.match(/mac/i);
    this.enable();

    this.shortcut("Cmd+C", { propagate: true });
    this.shortcut("Ctrl+C", { propagate: true });
    this.shortcut("Cmd+K");
    this.shortcut("Cmd+B");
    this.shortcut("Cmd+F");
    this.shortcut("Opt+Up");
    this.shortcut("Opt+Down");
    this.shortcut("Opt+Enter");
    this.shortcut("Cmd+Shift+M");
    this.shortcut("Cmd+Shift+J");
    this.shortcut("Cmd+Shift+K");
    this.shortcut("Cmd+Shift+H");
    this.shortcut("Enter");
    this.shortcut("Esc");
    this.shortcut("Tab");
    for (var i = 0; i < 10; i++) {
      this.shortcut("Cmd+"+i);
      if (!this.isMac) this.shortcut("Opt+"+i);
    }
  },

  shortcut: function(name, options) {

    var meta = this.isMac ? "Meta" : "Ctrl";

    var keystroke = name.replace("Cmd", meta).replace("Opt", "Alt"),
        method = "on" + name.replace(/\+/g, "");

    window.shortcut.add(keystroke, function(event) {
      if (this.enabled) {
        this.activeWindow = this.application.activeWindow();
        if (method.match(/\d$/)) {
          this.onNumeric.call(this, event, method.substr(-1));
        }
        else {
          this[method].call(this, event);
        }
        delete this.activeWindow;
      }
    }.bind(this), options);
  },

  onNumeric: function(event, number) {
    var win = this.application.nth_window(number);
    if (win) win.focus();
  },

  onCmdC: function(event) {
    if (!this.activeWindow.input.focused)



( run in 0.779 second using v1.01-cache-2.11-cpan-39bf76dae61 )