User:Whiteknight/designer3.js

From Wikibooks, open books for an open world
Jump to navigation Jump to search
Note: After saving, changes may not occur immediately. Click here to learn how to bypass your browser's cache.
  • Mozilla / Firefox / Safari: hold down Shift while clicking Reload, or press Ctrl-Shift-R (Cmd-Shift-R on Apple Mac);
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5;
  • Konqueror: simply click the Reload button, or press F5;
  • Opera users may need to completely clear their cache in Tools→Preferences.
//Visual Book designer (vbd) class
var vbd = {
  // Version numbers
  _require: 2.81,
  version:  3.73,
  bookpageversion: 0.00,
  pageheadversion: 0.00,

  // default name of all new books:
  pageTree: null,
  defName: 'New Book',
  defpagename: 'New Page',
  defheadname: 'New Heading', 
  newpagecnt: 1,
  newheadcnt: 1,

  //div IDs where the gadget is inserted into the page
  formspan: "WKVBDSpan",
  statspan: "WKVBDStatSpan",

  // Navigation templates and configuration parameters
  templates: new Array(
    ["Simple Header",    0, 0, "Simple header"],
    ["Page Nav Header",  1, 0, "Header with forward/back links"],
    ["Page Nav Header2", 1, 0, "Header with forward/back links (2)"],
    ["Page List Header", 0, 1, "Header with page list"],
    ["Page List Nav",    1, 1, "Header with page list and forward/back links"]
  ),
  template: 0,

  // Reading levels
  readingLevels: new Array(
    'Pre-Reader', 
    'Beginner', 
    'Intermediate', 
    'Advanced', 
    'Professional'
  ),
  readingLevel: 2,

  // Prerequisite and corequisite books
  prerequisites: new Array(),
  corequisites: new Array(),

  // Subjects for categorization
  subjects: new Array(),

  // whether to include certain special pages in the book by default
  useresources:    false,
  uselicensing:    false,
  useintroduction: false,
  usecollectionpreface: true,

  // other options
  defaultinherit:  false,
  defaultcollapse: false,
  commentsaspages: false,
  useexternaledit: false,
  
  // Drag-n-drop values
  lastClicked: null,
  offsetX: 0,
  offsetY: 0,
  startX: 0,
  startY: 0,
  box: null
};

//Basic array management functions
vbd.CopyArray = function(array) { return array.slice(0); }
vbd.FindPageNameInArray = function (array, name) {
  for(var i = 0; i < array.length; i++) {
    if(array[i].pagename == name) return i;
  }
  return -1;
}
vbd.FindHeadNameInArray = function(array, name) {
  for(var i = 0; i < array.length; i++) {
    if(array[i].label == name) return i;
  }
  return -1;
}

//Make a checkbox that corresponds to a boolean flag in vbd
vbd.makeOptionsCheckbox = function (field) {
  var cbox = wk.makeElement('input', {type: "checkbox"});
  cbox.checked = vbd[field];
  cbox.onclick = function () { vbd[field] = cbox.checked; }
  return cbox;
}

//Initialization functions. If the wk object is not found, the noinit method is called. Otherwise, the
//initialize method is called to start the gadget.
vbd.noinit = function() {
  wk.spanText(vbd.formspan, "Cannot load VBD. WKGadgetsCore is missing or is wrong version. "
    + "Please install WKGadgetsCore, or update it to version " + vbd._require + ".");
}
vbd.initialize = function () {
  vbd.pageTree = new BookPage(vbd.defName);
  vbd.status = wk.spanEditor(vbd.statspan);
  vbd.status("Use the outline tool below to create a new book");
  var query = wk.getQueryHash();
  if(typeof query["book"] == "undefined") {
    vbd.visual();
    return;
  }
  vbd.status("Loading the outline for " + query["book"] + "...");
  wk.loadWikiText(query["book"], function (text) { 
    var start = text.indexOf('{{User:Whiteknight/SavedOutlineStart}}');
    var end = text.indexOf('{{User:Whiteknight/SavedOutlineEnd}}');
    if(start == -1 || end == -1) {
      vbd.visual();
      return;
    }
    vbd.loadNodeTree(text.substring(start + 38, end));
    vbd.status("Use the outline tool below to create a new book");
  });
}
if(typeof wk == 'object' && wk.testVersion(vbd._require)) {
  addOnloadHook(vbd.initialize);
} else {
  addOnloadHook(vbd.noinit);
}

//rebuild the outline
vbd.visual = function() {
  vbd.box = wk.spanText(vbd.formspan, "");
  if(vbd.box == null) return;
  vbd.box.appendChild(vbd.pageTree.makeNode());
}

//Clear the outline completely and create a new one
vbd.clear = function() {
  vbd.pageTree = new BookPage(vbd.defName);
  vbd.subjects = new Array();
  vbd.readingLevel = 2;
  vbd.newpagecnt = 1;
  vbd.newheadcnt = 1;
  vbd.visual();
}

//Load a saved outline from a save page
vbd.loadNodeTree = function (text) {
  vbd.clear();
  var lastn = 0;
  var last = new Array();
  last[0] = vbd.pageTree;
  var lines = text.split("\n");
  for(i = 0; i < lines.length; i++) {
    lines[i] = lines[i].replace(/^[ \t]+/, "");
    lines[i] = lines[i].replace(/[ \t]+$/, "");
    lines[i] = lines[i].replace(/\r\n/g, "");
    if(lines[i] == "") continue;
    if(lines[i].match(/^=/)) { //heading
      var levels = lines[i].match(/^=+/);
      var n = levels[0].length;
      var k = n - 1;
      var head = new PageHeading(lines[i].substr(n));
      last[k].addHeading(head);
      last[k] = head;
      lastn = k;
    } else if(lines[i].match(/^\*+/)) { //subpage
      var stars = lines[i].match(/^\*+/);
      var n = stars[0].length;
      var k = n - 1;
      if(last[k] == null) { k = 0; }
      last[n] = new BookPage(lines[i].substr(n));
      last[k].addSubpage(last[n]);
      lastn = n;
    } else if(lines[i].match(/^&/)) { //pagetext
      last[lastn].pagetext += lines[i].substr(1);
    } else if(lines[i].match(/^%/)) { //comment
      last[lastn].comments += lines[i].substr(1);
    } else {  //main title
      vbd.pageTree.pagename = lines[i];
      lastn = 0;
    }
  }
  vbd.pageTree.formspan.innerHTML = "";
  vbd.visual();
  vbd.pageTree.formspan.appendChild(document.createTextNode(
    'Successfully loaded saved outline'
  ));
}

//Try to load in a saved collection
vbd.loadNodeTreeCollection = function(text) {
  vbd.clear();
  var last = vbd.pageTree;
  var lines = text.split("\n");
  for(var i = 0; i < lines.length; i++) {
    if(lines[i].match(/^===/) || lines[i].match(/\[\[category:/i)) {
      continue;
    } else if(lines[i].match(/^==.+==/)) {
      vbd.pageTree.pagename = vbd.extractHeadingName(lines[i]);
    } else if(lines[i].match(/^;/)) {
      var head = new PageHeading(lines[i].substring(1));
      last.addHeading(head);
      last = head;
    } else if(lines[i].match(/^:\[\[/)) {
      if(lines[i].match(/Resources/)) { vbd.useresources = true; continue; }
      if(lines[i].match(/Licensing/)) { vbd.uselicensing = true; continue; }
      if(lines[i].match(/Wikibooks:/)) { vbd.usecollectionpreface = true; continue; }
      var page = new BookPage(vbd.extractLinkPageName(lines[i], vbd.pageTree.pagename));
      last.addSubpage(page);
    }
  }
  vbd.visual();
  vbd.pageTree.formspan.innerHTML = 'Successfully loaded collection!';
}

//take the wikitext of an arbitrary page and try to load a reasonable outline from it.
vbd.loadNodeTreeTOC = function (text, title) {
  vbd.clear();
  var lastn = 0;
  var last = new Array();
  last[0] = vbd.pageTree;
  vbd.pageTree.pagename = title;
  var lines = text.split("\n");
  for(i = 0; i < lines.length; i++) {
    lines[i] = lines[i].replace(/^[ \t]+/, "");
    lines[i] = lines[i].replace(/[ \t]+$/, "");
    lines[i] = lines[i].replace(/\r\n/g, "");
    if(lines[i] == "") continue;
    if(lines[i].match(/^===.+===/)) { //heading
      var head = new PageHeading(vbd.extractHeadingName(lines[i]));
      last[0].addHeading(head);
      last[0] = head;
    } else if(lines[i].match(/^[\*\#]*\s*\[\[.+\]\]/)) { //subpage
      if(lines[i].match(/Resources/)) { vbd.useresources = true; continue; }
      if(lines[i].match(/Licensing/)) { vbd.uselicensing = true; continue; }
      if(lines[i].match(/Category:/i)) continue;
      var stars = lines[i].match(/^[\*\#]*/);
      var n = stars[0].length;
      if(n == 0) n = 1;
      var k = n - 1;
      if(last[k] == null) { k = 0; }
      last[n] = new BookPage(vbd.extractLinkPageName(lines[i], title));
      last[k].addSubpage(last[n]);
      lastn = n;
    }
  }
  vbd.pageTree.formspan.innerHTML = "";
  vbd.visual();
  vbd.pageTree.formspan.appendChild(document.createTextNode(
    'Successfully loaded outline from ' + title + ' TOC!'
  ));
}

//try to get the name of a level-3 heading
vbd.extractHeadingName = function(line) {
  line = line.substring(3);
  line = line.substring(0, line.indexOf("==="));
  return line;
}

//Try to get the page name from a link. Very limited ability to deal with relative links
vbd.extractLinkPageName = function(line, title) {
  line = line.substring(line.indexOf("[[") + 2);
  var end = line.indexOf("|");
  if(end == -1) {
    end = line.indexOf("]]");
  }
  line = line.substring(0, end)
  if(line.charAt(line.length - 1) == '/') line = line.substring(0, line.length - 1);
  if(line.charAt(0) == '/') line = title + line;
  for(end = line.indexOf("/") ; end != -1; end = line.indexOf("/")) {
    line = line.substring(end + 1);
  }
  return line;
}

//when saving an outline, find and remove an old save, if one is located on the page.
vbd.killOldSaves = function(text) {
  var start = text.indexOf('{{User:Whiteknight/SavedOutlineStart}}');
  var end = text.indexOf('{{User:Whiteknight/SavedOutlineEnd}}');
  if(start == -1 || end == -1) return text; //no previous save
  var prefix = text.slice(0, start);
  var suffix = text.slice(end + 36);
  return prefix + suffix;
}

//force a page name to use appropriate capitalization
vbd.forceCaps = function(title, isRoot) {
  if(isRoot) return vbd.forceTitleCaps(title);
  else return vbd.forceFirstCaps(title);
}

//determines if the word needs to be capitalized. Returns 1 if it should be, 0 otherwise.
vbd.isRealWord = function (word) {
  var preps = new Array('the', 'in', 'of', 'for', 'to', 'is', 'a', 'an');
  for(var i = 0; i < preps.length; i++) {
    if(word == preps[i]) return 0;
  }
  return 1;
}

//book names are forced to title caps
vbd.forceTitleCaps = function(title) {
  title.replace("_", " ");
  var words = title.split(" ");
  for(var i = 0; i < words.length; i++) {
    if(words[i].length > 3 || i == 0 || vbd.isRealWord(words[i]))
      words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
  }
  title = words.join(" ");
  return title;
}

//page names are forced to have the first letter capitalized, but are not forced to have title-caps
vbd.forceFirstCaps = function(title) {
  title.replace("_", " ");
  return title.charAt(0).toUpperCase() + title.slice(1);
}

//Create the wikitext of the selected navigation template
vbd.makeTemplateText = function () {
  var template = vbd.templates[vbd.template];
  var book = vbd.pageTree.pagename;
  var text = "{"+"{subst:User:Whiteknight/" + template[0] + "|" + 
    ((template[1])?("book="):("")) + book;
  if(template[2] == 1) {
    text += "|" + ((template[1] == 1)?("list="):("")) + vbd.pageTree.makeTemplateLinks();
    text += "[[" + book + "/Resources|Resources]] - " + 
            "[[" + book + "/Licensing|Licensing]] - " +
            "[[Talk:" + book + "|Discuss]]";
  }
  return text + "}}";
}

//<noinclude>

// XXX: Drop-down menu utilities. These aren't implemented yet.

//vbd.createDropMenu = function(parent, children) {
//  var menu = wk.makeElement('div', {style:"position:absolute; top: 0px; left: 0px; z-index: 10; background-color: white;"});
//  wk.appendChildren(menu, children);
//  menu.onmouseout = function() {
//    parent.removeChild(menu);
//  }
//  parent.appendChild(menu);
//}

// XXX: Drag'n'Drop utilities. These aren't implemented yet.

//document.addEventListener("mouseup", function(event) {
//  if(vbd.lastClicked &&
//     event.eventPhase == Event.BUBBLING_PHASE &&
//     event.currentTarget == event.target) {
//    vbd.lastClicked.box.style.border = "none";
//    vbd.lastClicked = null;
//  }
//}, false);

//vbd.extractNumber = function(value) {
//  var n = parseInt(value);
//  return n == null || isNaN(n) ? 0 : n; 
//}

//vbd.moveMouse = function(e) {
//  vbd.lastClicked.box.style.left = (vbd.offsetX + e.clientX - vbd.startX) + 'px';
//  vbd.lastClicked.box.style.top = (vbd.offsetY + e.clientY - vbd.startY) + 'px'; 
//  vbd.status("x:" + vbd.lastClicked.box.style.left + " y: " + vbd.lastClicked.box.style.top);
//}

//</noinclude>