view static-files/wiki.js @ 27:a42400d52a1e

Changed recent changes heading to two separate 'unpublished changes' and 'published changes' sections.
author Atul Varma <varmaa@toolness.com>
date Thu, 12 Feb 2009 23:00:03 -0800
parents 0aecc756ea18
children
line wrap: on
line source

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ubiquity.
 *
 * The Initial Developer of the Original Code is Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Atul Varma <atul@mozilla.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

var App = {
  PART_SEPARATOR: "\n\n",
  creole: null,
  editingElement: null,
  latestPublishedChangeset: null,
  localChanges: [],
  eventHandlers: {}
};

App.eventHandlers.editPart = function editPart(aEvt) {
  if (!aEvt.shiftKey)
    return;

  aEvt.preventDefault();

  App.enterEditMode([this], 1);
};

App.getMarkup = function getMarkup(parts) {
  var partsMarkup = [];
  $(".creole-markup", parts).each(
    function(i) { partsMarkup.push($(this).text()); }
  );

  return partsMarkup.join(App.PART_SEPARATOR);
};

App.saveChanges = function saveChanges(options) {
  var isUserChange = options.isUserChange;
  var date = options.date ? new Date(options.date) : new Date();
  var container;
  if (isUserChange)
    container = $('#unpublished-changes');
  else
    container = $('#published-changes');

  var markup = App.getMarkup($(window.document));
  var changeset = {date: date,
                   content: markup};
  App.localChanges.push(changeset);
  var changesetElem = $('<div class="changeset selected"></div>');
  function setText() {
    changesetElem.text(jQuery.timeago(changeset.date));
  }
  window.setInterval(setText, 60000);
  setText();
  changesetElem.mousedown(
    function(aEvt) {

      aEvt.preventDefault();
      if (aEvt.shiftKey) {
        $(window.document).find('.changeset').removeClass('diffed');
        var newerVersion = App.getMarkup($("#content"));
        var diff = $('<div class="diff"></div>');
        var markupDiv = $('<div class="creole-markup"></div>');
        markupDiv.text(newerVersion);
        diff.html(diffString(changeset.content, newerVersion));
        $("#content").empty();
        $("#content").append(diff);
        $("#content").append(markupDiv);
        $(this).addClass('diffed');
      } else {
        $(window.document).find('.changeset').removeClass('selected')
                                             .removeClass('diffed');
        $(this).addClass('selected');
        $("#content").empty();
        $("#content").append(App.createParts(changeset.content));
      }
    }
  );
  $(window.document).find('.changeset').removeClass('selected')
                                       .removeClass('diffed');
  container.find('h1').after(changesetElem);
  if (isUserChange) {
    var maybeNewChangeset = App.latestPublishedChangeset + 1;
    var jsonData = JSON.stringify({date: changeset.date.toString(),
                                   content: changeset.content});
    jQuery.ajax({url: '/' + maybeNewChangeset,
                 type: 'PUT',
                 success: function() {
                   // TODO: Because we're running asynchronously,
                   // weird things can happen here, e.g. if the user
                   // makes another edit while the XHR is being
                   // processed.
                   App.latestPublishedChangeset = maybeNewChangeset;
                   $('#published-changes').find('h1').after(changesetElem);
                 },
                 error: function(xhr, textStatus, errorThrown) {
                   // TODO: Do something here.
                 },
                 dataType: 'text',
                 processData: false,
                 contentType: 'application/json',
                 data: jsonData});
  }
};

App.enterEditMode = function enterEditMode(parts, level, cursorPos) {
  if (App.editingElement) {
    $(App.editingElement).blur();
    App.editingElement = null;
  }

  var editablePart = $('<textarea class="wiki-edit"></textarea>').get(0);
  var originalMarkup = App.getMarkup(parts);
  $(editablePart).attr("value", originalMarkup);

  function setScrollHeight() {
    $(this).height(this.scrollHeight);
  }

  function exitEditMode() {
    var markup = $(this).attr("value");
    $(this).replaceWith(App.createParts(markup));
    if (markup != originalMarkup) {
      App.saveChanges({isUserChange: true});
    }
  }

  function editSiblings(aEvt) {
    if (!aEvt.shiftKey || level == 0)
      return;

    aEvt.preventDefault();

    var prevMarkup = App.getMarkup($(this).prevAll());
    var cursorPos = (prevMarkup.length + App.PART_SEPARATOR.length +
                     this.selectionStart);

    var allSiblings = this.parentNode.childNodes;
    exitEditMode.apply(this);

    App.enterEditMode(allSiblings, level-1, cursorPos);
  }

  $(editablePart).blur(exitEditMode);
  $(editablePart).mousedown(editSiblings);
  $(editablePart).keyup(setScrollHeight);
  $(parts).filter(":not(:first)").remove();
  $(parts).replaceWith(editablePart);
  setScrollHeight.apply(editablePart);

  if (typeof(cursorPos) != "undefined") {
    editablePart.selectionStart = cursorPos;
    editablePart.selectionEnd = cursorPos;
  }

  App.editingElement = editablePart;
  $(editablePart).focus();
};

App.createPart = function createPart(markup) {
  var partDiv = $('<div class="part"></div>');
  var markupDiv = $('<div class="creole-markup"></div>');
  markupDiv.text(markup);
  App.creole.parse(partDiv.get(0), markup);
  partDiv.mousedown(App.eventHandlers.editPart);
  partDiv.append(markupDiv);
  return partDiv.get(0);
};

App.createParts = function createParts(text) {
  var parts = [];

  var partsMarkup = text.split(App.PART_SEPARATOR);
  jQuery.each(
    partsMarkup,
    function(i) {
      var partMarkup = this.toString();
      if (partMarkup)
        parts.push(App.createPart(partMarkup));
    }
  );

  return parts;
};

App.eventHandlers.onLoad = function onLoad() {
  App.creole = new Parse.Simple.Creole(
    {interwiki: {
       WikiCreole: 'http://www.wikicreole.org/wiki/',
       Wikipedia: 'http://en.wikipedia.org/wiki/'
     },
     linkFormat: ''
    });

  function onStatus(status) {
    App.latestPublishedChangeset = status.max_changeset;
    jQuery.get("/" + status.max_changeset,
               {},
               function(obj) {
                 var text = obj.content;
                 $("#content").append(App.createParts(text));
                 App.saveChanges({isUserChange: false,
                                  date: obj.date});
               },
               "json");
  }

  jQuery.get('/status', {}, onStatus, "json");
};

$(window).ready(App.eventHandlers.onLoad);