Customize Vista Edit
Nov 13th, 2009 by Hoi Lo
Customize Vista Edit
Prepared By:
Henry Lo
ABSTRACT
The Customize Vista Edit is a script that adds to the features found when editing a course in the Build mode of UBC Vista courses. When installed onto Greasemonkey, the Customize Vista Edit allows course builders to: quick or full preview their HTML code, revert to previous code, and expand the textbox for better viewing. This report is focused on the design and development of the Customize Vista Edit script, and will elaborate on its included features.
Table of Contents
ABSTRACT. 2
1.0 INTRODUCTION.. 5
2.0 BACKGROUND.. 6
3.0 CODING.. 7
3.1 FormParams. 7
3.2 Quick Preview.. 8
3.3 Save Code. 9
3.4 Revert 10
3.5 Resize. 10
3.6 Full Preview.. 13
3.7 Layout 13
4.0 CONCLUSION.. 15
APPENDIX I 16
Table of Figures
Figure 1. Greasemonkey – Manage Script 5
Figure 2. Customize Vista Edit layout 5
Figure 3. Quick Preview.. 8
Figure 4. Save Buttons. 9
Figure 5. Revert Prompt 10
Figure 6. Revert Button. 10
Figure 7. Textbox: Expand. 12
Figure 8. Textbox: Reset 12
Figure 9. Layout 14
1.0 INTRODUCTION
The Customize Vista Edit script is an intuitive addition to the course editor found in the build mode of UBC Vista. To use it, the user must have the Mozilla Firefox add-on Greasemonkey already running on their browser. Greasemonkey is a program that allows users to install scripts that make on-the-fly changes to HTML webpage content on the DOMContentLoaded event, which happens immediately after it is loaded in the browser. For example, downloading a text area size adjuster onto Greasemonkey will allow users to adjust any textboxes found in any website.
To download the Customize Vista Edit, use the following link:
http://oltubc.com/customizevistaedit.user.js
Figure 1. Greasemonkey – Manage Script
After completing the installation, enabling the script will activate it. From now on, whenever the user enters the build mode and edits a course report, for example, the Customize Vista Edit toolbar will appear with new options for the user that will allow for more convenient editing.
Figure 2. Customize Vista Edit layout
2.0 BACKGROUND
The Customize Vista Edit script is mostly developing with JavaScript coding.
JavaScript is an object-oriented scripting language used to enable programmatic access to objects within both the client application and other applications. It is primarily used as an integrated component of the web browser, allowing the development of enhanced user interfaces and dynamic websites.
3.0 CODING
This section of the report will begin discussing the development of the Customize Vista Edit script. Each section will focus on the features added by Customize Vista Edit and will take an in-depth analysis and explain how each feature was developed and help you better understand the code behind it. (Refer to Appendix I)
3.1 FormParams
The FormParams function is developed to take a form and translates the elements into an AJAX ready sequence of parameters. The reason for implementing this function is to allow the save function, discussed later, to save changes without having to exit the page every time. This would allow the user to make quick changes and preview the code and make changes conveniently. The inputs that the FormParams function scans are separated into two types, regular form inputs (title), and text areas (HTML textbox). The code for retrieving the inputs are as follows:
// Get all Inputs
var finputs = form.getElementsByTagName(‘input’);
var farray = new Array();
for(var i = 0; i < finputs.length; i++){
if(finputs[i].name.length > 0){
farray[i] = finputs[i].name + “=” + encodeURI(finputs[i].value);
}
}
The function first assigns the input tag to the variable finputs. Then, it will enter a ‘for’ loop in which it will scan each character of the input and translates the elements into AJAX. Scanning the text areas is the same.
// Get all Textareas
var farray2 = new Array();
var ftextarea = form.getElementsByTagName(‘textarea’);
for(var i = 0; i < ftextarea.length; i++){
if(ftextarea[i].name.length > 0){
farray2[i] = ftextarea[i].name + “=” + encodeURIComponent(ftextarea[i].value);
}
}
3.2 Quick Preview
The preview button found on the Customize Vista Edit layout is used to generate a demo page. This allows users to quickly view their current page and make changes. The function starts out by scanning the HTML textbox using the method getElementById(). This method returns a reference to the first object with the specified ID, which in this case is ‘htmlText’. Next, it takes the values inside the textbox using textarea.value and stores it, as values, into ttransfer.
// Get the html source from textarea
var textarea = document.getElementById(‘htmlText’);
var semiwrap = document.createElement(‘div’);
var ttransfer = textarea.value;
Then it transfers the values in ttransfer and sets the HTML syntax describing the element’s descendants into the variable semiwrap using .innerHTML. This method takes the text and converts it into HTML format:
semiwrap.innerHTML = ttransfer;
document.getElementById(‘wrap’).innerHTML = semiwrap.innerHTML;
Figure 3. Quick Preview
Due to the nature of Vista’s addressing issues, much of the source code from the textbox is different; therefore some measures have been taken to fix links and images before preview. The below code implements this:
// Replace image and link paths for local view
ttransfer = ttransfer.replace(/(src=(\”|\’))(\.\.\/)?([0-9a-zA-Z\/_?+-.]+)(\”|\’)/g, “$1” + newroot + folder + “/$3$4$5”);
ttransfer = ttransfer.replace(/(href=(\”|\’))(\.\.\/)?([0-9a-zA-Z\/_?+-.]+)(\”|\’)/g, “$1” + newroot + folder + “/$3$4$5”);
3.3 Save Code
The save code function is a simple algorithm that allows the user to save their current source code by means of AJAX http protocols. By using AJAX, preventions allow Vista from redirecting back to the item menu allowing further changes if the last edits were not satisfactory. The code start by requesting the http protocol:
var http = new XMLHttpRequest();
if(http.overrideMimeType)
{
http.overrideMimeType(‘text/html’);
}
Next, it retrieves the parameters and initializes the header utilizing the formparams function discussed earlier. Now space has been created to save the current code. The following code sends the header information and waits for the http to respond, letting the user know that their code has been successfully saved.
// Get form parameters and action
var params = unsafeWindow.formparams(document.forms[0]);
var url = unsafeWindow.formaction(document.forms[0]);
// Initialize a header
http.open(“POST”, root + url, true);
// Send Header Information
http.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
http.setRequestHeader(“Content-length”, params.length);
http.setRequestHeader(“User-Agent”, “Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11”);
http.setRequestHeader(“Accept-Charset”, “ISO-8859-1,utf-8;q=0.7,*;q=0.7”);
http.setRequestHeader(“Keep-Alive”, “300”);
http.setRequestHeader(“Connection”, “close”);
// Wait for a http response
http.onreadystatechange = function() {//Call a function when the state changes.
if(http.readyState == 4 && http.status == 200) {
alert(“Changes Saved”);
}
}
// end onreadystatechange
http.send(params);
Figure 4. Save Buttons
3.4 Revert
The “Revert” button is to allow users that have made a mistake when writing the HTML source code to go back to the version of the source code that first appeared in the current session. The code is very simple for the implementation of this feature. First, the function scans the HTML textbox using the method getElementById(). This method returns a reference to the first object with the specified ID, which in this case is ‘htmlText’.
// oCode = Original Code
var rtextarea = document.getElementById(‘htmlText’);
Then it prompts a warning message asking “Are you sure you want to revert the document”, if the user clicks yes, the code will return to the code present at the start of the current session, where oCode is the original source code.
Figure 5. Revert Prompt
// Make sure the user really wants to undo ALL the modifications
var answer = confirm(“Are you sure you want to revert the document?”, 2, 2);
// I think the answer comes in binary / bool
if(answer){
rtextarea.value = oCode; }
Figure 6. Revert Button
3.5 Resize
The resize function is a more complex algorithm as it utilizes both javascript and CSS coding in the expansion of the HTML textbox. CSS, Cascading Style Sheets, is designed primarily to enable the separation of document content (written in HTML or a similar markup language) from document presentation, including elements such as the colors, fonts, and layout. This separation can improve content accessibility and provide more flexibility and control. The reason for using CSS is to improve the layout. When using JavaScript coding for the expansion of the textbox, the box collides with other items on the page, causing unwanted obstructions. As CSS is designed for improving page layout, its usefulness in this case is unquestionable. First, initialization of CSS to be used within Greasemonkey is required, this is demonstrated by the below code:
// inserts the ability to use CSS stylex
// =====================================
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName(‘head’)[0];
if(!head) {return;}
style = document.createElement(‘style’);
style.type = ‘text/css’;
style.innerHTML = css;
head.appendChild(style); }
This code allows the use of CSS styles within the script. Next we can adjust the height and width of the textbox by first specifying its class and then adjust the size accordingly.
unsafeWindow.resizefcn = function (){
// Inserts the ability to edit the style of the page with CSS
addGlobalStyle(‘.descForm {height: 500px; width: 700px;}’);
}
In addition, the feature to change back to the original size was implemented. After the user resizes the textbox, the button will change from Textbox: Expand to Textbox: Reset. This is accomplished by including the following code in the resize function:
resize.value = “Textbox: Reset”;
resize.setAttribute(“onClick”, “resetfcn()”);
thediv.appendChild(reset);
And the following code in the reset function, which resets the textbox size back to the original:
// Inserts the ability to edit the style of the page with CSS
addGlobalStyle(‘.descForm {height: 120px; width: 400px;}’);
resize.value = “Textbox: Expand”;
resize.setAttribute(“onClick”, “resizefcn()”);
thediv.appendChild(resize);
Figure 7. Textbox: Expand
Figure 8. Textbox: Reset
3.6 Full Preview
This function generates a full popup window preview of the current page. This function first grabs the current page ID and then gets the page address along with its parameters followed by opening the popup window. These steps are implemented by the following code:
// Grab the current page ID
var pageid = document.getElementsByName(‘pageID’)[0].value;
var page = document.getElementsByName(‘linkedToFilePath’)[0];
// Get the page link along with it’s parameters
var viewer = “https://www.vista.ubc.ca/webct/ContentPageServerServlet/” + page.value + “?pageID=” + pageid;
// Open the popup window
window.open(viewer,’popup_preview’,’width=800,height=640,menubar=1,toolbar=1,scrollbars=1,status=1,location=1,resizable=1′)
3.7 Layout
Now, we need buttons for the features we implemented above. To create these buttons using JavaScript, we first start by initializing variables and values according to each button.
var preview = cancel.cloneNode(true);
preview.value = “Full Preview”;
var save = cancel.cloneNode(true);
save.value = “Save Changes”;
var revert = cancel.cloneNode(true);
revert.value = “Revert”;
var resize = cancel.cloneNode(true);
resize.value = “Textbox: Expand”;
Next, we assign the functions to their respective buttons:
preview.setAttribute(“onClick”, “fullPreview()”);
save.setAttribute(“onClick”, “saveCode()”);
revert.setAttribute(“onClick”, “revert(originalCode)”);
resize.setAttribute(“onClick”, “resizefcn(originalCode)”);
With the buttons created, we now create the layout. To have spaces between each button, we use the following code:
// aspan is just a little gap I use to separate
// the custom buttons I created
for(var i = 0; i < 2; i++){
aspan = aspan.cloneNode(true);
thediv.appendChild(aspan);}
The final product is:
Figure 9. Layout
4.0 CONCLUSION
The Customize Vista Edit script is an intuitive and convenient addition to the course editor in UBC Vista. With its addition, users can quick preview their source code, save their current code without needing to exit the page, resize and rest the textbox, and revert to the original source code. The development of the script utilized both JavaScript and CSS languages to implement the features of the Customize Vista Edit. The Customize Vista Edit script is aimed to convenient web designer in UBC Vista, and additional features are being considered to allow users a more streamline experience during web development.
APPENDIX I
// ==UserScript==
// @name Customize Vista Edit
// @namespace Customize Vista Edit
// @include https://www.*.ubc.ca/webct*editOrganizerResource.dowebct?*selectedResourceType=PAGE_TYPE*
// @include https://www.*.ubc.ca/webct*viewCourseMapItemDispatcher.dowebct?*&tab=build&targetFrame=RIGHTTOOLFRAME*
// @include https://www.*.ubc.ca/webct*linkedToResource.dowebct?*&selectedResourceType=PAGE_TYPE&selectedResourceID=*
// ==/UserScript==
// Last Edit: April 28th, 2008
// ===========================
// Thoughts…
// :: Some possible functions to implement – Detect when target site doesn’t really save any changes to document
// :: Modify to use AJAX frameworks (eg, jQuery) for easy upgrade
// :: Port this code to a Safari/Greasekit for the stupid Macs, main modifictions would be to change unsafeWindow element
// to GreaseKit compliant object
// Active Editing
// ==============
// If turned onto true, quick preview changes will be updated
// in real time, however, it’s fairly slow. Turned off for efficiency
var active = false;
var h = 500 + “px”;
var w = 500 + “px”;
var count=0;
// Vista Server
var root = “https://www.vista.ubc.ca”;
// File Root Folder Declaration
var folder = document.getElementById(‘folderPath’).value.split(“/”)[1];
// Create an invisible DIV Wrapper for easy js access later
var wrap = document.createElement(“div”);
wrap.setAttribute(“id”, “wrap”);
// inserts the ability to use CSS styles
// =====================================
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName(‘head’)[0];
if(!head) {return;}
style = document.createElement(‘style’);
style.type = ‘text/css’;
style.innerHTML = css;
head.appendChild(style);
}
/*
// Custom Function getElementsByClassName
// ======================================
// – This should’ve been prototyped for elements but
// for the purposes, this should be okay
// Takes in the name from the “class=” attribute
document.getElementsByClassName = function(clsName){
var retVal = new Array();
var elements = document.getElementsByTagName(“*”);
for(var i = 0;i < elements.length;i++){
if(elements[i].className.indexOf(” “) >= 0){
var classes = elements[i].className.split(” “);
for(var j = 0;j < classes.length;j++){
if(classes[j] == clsName)
retVal.push(elements[i]);
}
}
else if(elements[i].className == clsName)
retVal.push(elements[i]);
}
return retVal;
}
*/
// unsafeWindow :: FormParams
// ==========================
// Takes a form and translates the elements into a AJAX
// ready sequence of parameters (eg, name=jack&age=18&comment=…)
// Separated into two types, regular form inputs and textareas
// Class unsafeWindow due to user-action property
unsafeWindow.formparams = function(form){
// Get all Inputs
var finputs = form.getElementsByTagName(‘input’);
var farray = new Array();
for(var i = 0; i < finputs.length; i++){
if(finputs[i].name.length > 0){
farray[i] = finputs[i].name + “=” + encodeURI(finputs[i].value);
}
}
// Get all Textareas
var farray2 = new Array();
var ftextarea = form.getElementsByTagName(‘textarea’);
for(var i = 0; i < ftextarea.length; i++){
if(ftextarea[i].name.length > 0){
farray2[i] = ftextarea[i].name + “=” + encodeURIComponent(ftextarea[i].value);
}
}
return farray.join(“&”) + “&” + farray2.join(“&”);;
}
// unsafeWindow :: FormAction
// ==========================
// Takes a form and retrieves the “action=” parameter
// Once again, it’s for AJAX calls later
// Class unsafeWindow due to user-action property
unsafeWindow.formaction = function(form){
return form.getAttribute(“action”);
}
// unsafeWindow :: PreviewCode
// ===========================
// Function called by preview code button to generate a demo page
// Due to the nature of Vista’s addressing issues, much of the source
// code from textarea is different, therefore some measures have been
// taken place to fix these links before previewed (src for images,
// href for links and frames, etc)
// Class unsafeWindow due to user-action property
unsafeWindow.previewCode = function (){
// Get the html source from textarea
var textarea = document.getElementById(‘htmlText’);
var semiwrap = document.createElement(‘div’);
var ttransfer = textarea.value;
// Make a local root to avoid auto path prefix from browser
var newroot = “https://www.vista.ubc.ca/webct/RelativeResourceManager/Template/”;
// Replace image and link paths for local view
ttransfer = ttransfer.replace(/(src=(\”|\’))(\.\.\/)?([0-9a-zA-Z\/_?+-.]+)(\”|\’)/g, “$1” + newroot + folder + “/$3$4$5”);
ttransfer = ttransfer.replace(/(href=(\”|\’))(\.\.\/)?([0-9a-zA-Z\/_?+-.]+)(\”|\’)/g, “$1” + newroot + folder + “/$3$4$5”);
semiwrap.innerHTML = ttransfer;
document.getElementById(‘wrap’).innerHTML = semiwrap.innerHTML;
}
// unsafeWindow :: saveCode
// Save the source code by means of AJAX http protocols
// This prevents Vista from redirecting back to the item menu
// allowing further changes if the last edits were not satisfactory
// ** Important :: If for some peculiar reason the file is not saved
// it will still say “Changes Saved”, one way to fix this is to
// to scan the http.responseText for any error messages
// Class unsafeWindow due to user-action property
unsafeWindow.saveCode = function (){
var http = new XMLHttpRequest();
if(http.overrideMimeType){
http.overrideMimeType(‘text/html’);
}
// Get form parameters and action
var params = unsafeWindow.formparams(document.forms[0]);
var url = unsafeWindow.formaction(document.forms[0]);
// Initialize a header
http.open(“POST”, root + url, true);
// Send Header Information
http.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
http.setRequestHeader(“Content-length”, params.length);
http.setRequestHeader(“User-Agent”, “Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11”);
http.setRequestHeader(“Accept-Charset”, “ISO-8859-1,utf-8;q=0.7,*;q=0.7”);
http.setRequestHeader(“Keep-Alive”, “300”);
http.setRequestHeader(“Connection”, “close”);
// Wait for a http response
http.onreadystatechange = function() {//Call a function when the state changes.
if(http.readyState == 4 && http.status == 200) {
alert(“Changes Saved”);
}
}
// end onreadystatechange
http.send(params);
}
// unsafeWindow :: Revert
// ======================
// Session based revert function. This reverts any changes to the original
// source code that was first opened in the current session. This does Not
// revert to the last “working” save”.
// Class unsafeWindow due to user-action property
unsafeWindow.revert = function (oCode){
// oCode = Original Code
var rtextarea = document.getElementById(‘htmlText’);
// Make sure the user really wants to undo ALL the modifications
var answer = confirm(“Are you sure you want to revert the document?”, 2, 2);
// I think the answer comes in binary / bool
if(answer){
rtextarea.value = oCode;
}
}
// unsafeWindow :: Resize
// ======================
// Expands the textarea
// Class unsafeWindow due to user-action property
unsafeWindow.resizefcn = function (){
// Inserts the ability to edit the style of the page with CSS
addGlobalStyle(‘.descForm {height: 500px; width: 700px;}’);
resize.value = “Textbox: Reset”;
resize.setAttribute(“onClick”, “resetfcn()”);
thediv.appendChild(reset);
}
// unsafeWindow :: Reset
// ======================
// Resets the textarea
// Class unsafeWindow due to user-action property
unsafeWindow.resetfcn = function (){
// Inserts the ability to edit the style of the page with CSS
addGlobalStyle(‘.descForm {height: 120px; width: 400px;}’);
resize.value = “Textbox: Expand”;
resize.setAttribute(“onClick”, “resizefcn()”);
thediv.appendChild(resize);
}
// unsafeWindow :: FullPreview
// ===========================
// This function generates a full (popup) preview of the current
// page as if the user were to click on action menu -> preview page
// Class unsafeWindow due to user-action property
// User Prompted Action, must be of class unsafeWindow
unsafeWindow.fullPreview = function (){
// Grab the current page ID
var pageid = document.getElementsByName(‘pageID’)[0].value;
var page = document.getElementsByName(‘linkedToFilePath’)[0];
// Get the page link along with it’s parameters
var viewer = “https://www.vista.ubc.ca/webct/ContentPageServerServlet/” + page.value + “?pageID=” + pageid;
// Open the popup window
window.open(viewer,’popup_preview’,’width=800,height=640,menubar=1,toolbar=1,scrollbars=1,status=1,location=1,resizable=1′)
}
// ==================================================================================
// Figure out where we are and what we need
// Get the current address (to get the ID of the current page
var address = document.location.toString();
var rawid;
// Check which page we are in and get rawID
if(address.indexOf(“viewCourseMapItemDispatcher.dowebct”)!=-1){
rawid = address.split(“componentId=”)[1];}
else{
rawid = address.split(“selectedResourceID=”)[1];}
// … get rawID
var id = rawid.split(“&”)[0];
// For Active Editing Only
var textarea = document.getElementById(‘htmlText’);
// Activate real-time preview
// Warning :: Slow
if(active){
textarea.setAttribute(“onKeyUp”, “previewCode()”);}
// Adjust the differences between the annoying MAC
// and the awesome PC. For some reason, Vista renders
// differently on a MAC and PC
var div_size = 350;
var tdiv;
// Fix the Textarea Size;
if(navigator.appVersion.indexOf(“Win”)!=-1){
tdiv = document.getElementById(‘formTag’);
tdiv.style.height = div_size + “px !important”;
}
textarea.style.height = (div_size-15) + “px !important”;
textarea.style.width = “800px !important”;
// Expand the textarea to a bigger size for better editing
document.forms.namedItem(“editPageForm”).elements.namedItem(“htmlText”).setAttribute(‘rows’, 50);
// Fix the Page Squeeze Problem
mystyle = document.styleSheets[0];
// Save a copy of the original code before any modifications
unsafeWindow.originalCode = textarea.value;
// Create a break and insert our Quick Preview
document.body.appendChild(document.createElement(“br”));
document.body.appendChild(wrap);
// Start adding buttons from previous buttons
// supplied by Vista.
// Create & insert Exit button, this button does not
// save any changes.
var theform = document.forms[0];
var inputs = document.getElementsByTagName(‘input’);
var cancel = inputs[inputs.length-1];
cancel.value = “Exit”;
// Create & insert our Save button
// Rename our save and exit button
var oSave = inputs[inputs.length-2];
oSave.value = “Save and Exit”;
var preview = cancel.cloneNode(true);
var save = cancel.cloneNode(true);
var revert = cancel.cloneNode(true);
var thediv = cancel.parentNode;
// Create & insert our FullPreview button
preview.value = “Full Preview”;
save.value = “Save Changes”;
revert.value = “Revert”;
var aspan = document.createElement(“span”);
aspan.innerHTML = “ ”;
// Assign the buttons their functions
preview.setAttribute(“onClick”, “fullPreview()”);
save.setAttribute(“onClick”, “saveCode()”);
revert.setAttribute(“onClick”, “revert(originalCode)”);
// aspan is just a little gap I use to separate
// the custom buttons I created
for(var i = 0; i < 2; i++){
aspan = aspan.cloneNode(true);
thediv.appendChild(aspan);
}
thediv.appendChild(save);
// Add the Quick Preview Button
if(!active){
var quickpreview = cancel.cloneNode(true);
quickpreview.value = “Quick Preview”;
quickpreview.setAttribute(“onClick”, “previewCode()”);
for(var i = 0; i < 2; i++){
aspan = aspan.cloneNode(true);
thediv.appendChild(aspan);
}
thediv.appendChild(quickpreview);
}
// Add some Spaces
for(var i = 0; i < 2; i++){
aspan = aspan.cloneNode(true);
thediv.appendChild(aspan);
}
// Add the Full Preview Button
thediv.appendChild(preview);
// Add some extra Spaces
for(var i = 0; i < 2; i++){
aspan = aspan.cloneNode(true);
thediv.appendChild(aspan);
}
// Add the Revert Button
thediv.appendChild(revert);
// Add some extra Spaces
for(var i = 0; i < 2; i++){
aspan = aspan.cloneNode(true);
thediv.appendChild(aspan);
}
var resize = cancel.cloneNode(true);
resize.value = “Textbox: Expand”;
resize.setAttribute(“onClick”, “resizefcn()”);
thediv.appendChild(resize);
// Initialize a first Preview Code
unsafeWindow.previewCode();