view script.js @ 3:6ef82d6818f4

readme
author Fabien Cazenave <fabien@cazenave.cc>
date Mon, 19 Dec 2011 19:00:15 +0100
parents 683f56999fb3
children
line wrap: on
line source

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

/**
  * File          : barbecue.js
  * Author        : Fabien Cazenave <fabien@cazenave.cc>
  * Version       : 0.1
  * License       : MIT
  * Last Modified : 2011-12-19
  */

/** wysiwyg Editor
  * expected compatibility: all browsers except IE<9.
  * 
  * This HTML content editor is not intended to compete with CKEditor or
  * TinyMCE: it is designed to *test* the various implementations of
  * contentEditable and execCommand in modern browsers.
  *
  * Note: a specific micro-format is expected for the format toolbar.
  */
!function(window, document, undefined) {
  var
    gActiveEditor = null, // active editing host
    gCommandDump  = null; // command dump field (debug)

  function ExecCommand(toolbarElement) {
    var argVal, argStr,
      type    = toolbarElement.getAttribute("type"),
      command = toolbarElement.getAttribute("data-command");

    // get the execCommand argument according to the button type
    switch (type) {
      case "button":   // toolbar button: no argument
        argVal = argStr = false;
        break;
      case "checkbox": // styleWithCSS: boolean argument
        argVal = argStr = "" + toolbarElement.checked + "";
        break;
      default:         // <select> menu: string argument
        if (!toolbarElement.selectedIndex) return;
        argVal = toolbarElement.value;
        argStr = "'" + argVal.replace("<", "&lt;").replace(">", "&gt;") + "'";
        toolbarElement.selectedIndex = 0; // reset drop-down list
    }

    // send requested action and re-focus the editable element
    document.execCommand(command, false, argVal);
    if (gActiveEditor) gActiveEditor.focus();

    // debug
    gCommandDump.innerHTML = "document.execCommand('" + command + "', false, '" + argStr + "');";
  }

  window.addEventListener("DOMContentLoaded", function() {
    var i,
      buttons = document.querySelectorAll("*[data-command]"),
      editors = document.querySelectorAll("*[contenteditable]");

    // initialize all toolbar buttons
    for (i = 0; i < buttons.length; i++) {
      buttons[i].onclick  = function() { ExecCommand(this); };
      buttons[i].onchange = function() { ExecCommand(this); };
    }

    // remember last-focused editable element
    for (i = 0; i < editors.length; i++)
      editors[i].onfocus = function() { gActiveEditor = this; };

    // debug
    gCommandDump = document.querySelector("#execCommand");
    ExecCommand(document.querySelector("#useCSS"));
  }, false);
} (this, document);

/** DOMNodeFocused
  * expected compatibility: all browsers except IE<9.
  *
  * This custom (non-standard) event is fired when a node in an editable element
  * gets the user focus.
  */
!function(window, document, undefined) {
  var gLastFocusNode = null;

  function onCaretMove(event) {
    var selection = document.getSelection();
    var node = selection.isCollapsed ? selection.focusNode
             : selection.getRangeAt(0).commonAncestorContainer;
    if (node != gLastFocusNode) {
      gLastFocusNode = node;
      // fire a 'DOMNodeFocused' mutation event with bubbling to set the .target
      //var evtObject = document.createEvent("MutationEvent");
      //evtObject.initMutationEvent("DOMNodeFocused", true, false, node, "", "", "", 0);
      // note that a standard event works fine, too :-)
      var evtObject = document.createEvent("Event");
      evtObject.initEvent("DOMNodeFocused", true, false);
      node.dispatchEvent(evtObject);
    }
  }

  // trigger a 'DOMNodeFocused' event when the user moves the caret
  window.addEventListener("DOMContentLoaded", function() {
    var i, editors = document.querySelectorAll("*[contenteditable]");
    for (i = 0; i < editors.length; i++) {
      editors[i].addEventListener("keyup",   onCaretMove, false);
      editors[i].addEventListener("mouseup", onCaretMove, false);
    }
  }, false);
} (this, document);

/** Mutation Events -- demo!
  * expected compatibility: Firefox Aurora, possibly other modern browsers.
  * reference: https://developer.mozilla.org/en/XUL/Events#Mutation_DOM_events
  *            http://help.dottoro.com/ljifcdwx.php
  * 
  * SXE requires these (standard) mutation events:
  *   - 'new':    DOMNodeInserted
  *   - 'remove': DOMNodeRemoved
  *   - 'set':    DOMSubtreeModified, DOMAttrModified, DOMCharacterDataModified
  * 
  * Note: webkit doesn't support DOMAttrModified
  * https://bugs.webkit.org/show_bug.cgi?id=8191
  */
window.addEventListener("DOMContentLoaded", function() {
  var breadcrumb = document.querySelector("#breadcrumb");
  function updateBreadCrumb(event) {
    if (!breadcrumb || !event || !event.target) return;
    var
      body = document.body,
      node = event.target,
      text = node.nodeName,
      tmp  = node.parentNode;
    while (tmp && tmp != body) {
      text = tmp.nodeName + " > " + text;
      tmp = tmp.parentNode;
    }
    text = event.type + ": " + text;
    breadcrumb.innerHTML = text;
    console.log(text);
  }

  // listening to mutation events on 'document' freezes Firefox :-/
  var i, editors = document.querySelectorAll("*[contenteditable]");
  for (i = 0; i < editors.length; i++) {
    // set
    editors[i].addEventListener("DOMAttrModified",          updateBreadCrumb, false);
    editors[i].addEventListener("DOMSubtreeModified",       updateBreadCrumb, false);
    editors[i].addEventListener("DOMCharacterDataModified", updateBreadCrumb, false);
    // new, remove
    editors[i].addEventListener("DOMNodeInserted", updateBreadCrumb, false);
    editors[i].addEventListener("DOMNodeRemoved",  updateBreadCrumb, false);
    // focus (non-standard but can be useful)
    editors[i].addEventListener("DOMNodeFocused",  updateBreadCrumb, false);
  }
}, false);