# HG changeset patch # User Sean Halle # Date 1407580466 25200 # Node ID cc10d99e3f83f734356610fa4bcf930d84b91c69 # Parent 20a1407463a054d94014818ae0ebd41ba8a10263 Persist and read back from disk and parse to objects is working.. Display needs updating now diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/.idea/workspace.xml --- a/1__Development/0__Code_Dev/Javascript_approach/.idea/workspace.xml Thu Aug 07 00:10:50 2014 -0700 +++ b/1__Development/0__Code_Dev/Javascript_approach/.idea/workspace.xml Sat Aug 09 03:34:26 2014 -0700 @@ -2,9 +2,10 @@ + + - @@ -31,30 +32,10 @@ - - - - - - - - - - - - - - - - - - - - - + @@ -64,9 +45,36 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -81,34 +89,89 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - - - - + + + @@ -117,33 +180,44 @@ + + + + + + + + + + + + + + + + + + + + - - + + - + + - - + + - - - - - - - - - - - - + + @@ -171,13 +245,16 @@ @@ -200,7 +277,7 @@ diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/GabePattern/.~lock.12_Dc_02___Sample_Prog_to_hand_compile__with_syntax_graph__for_Gabe.odg# --- a/1__Development/0__Code_Dev/Javascript_approach/GabePattern/.~lock.12_Dc_02___Sample_Prog_to_hand_compile__with_syntax_graph__for_Gabe.odg# Thu Aug 07 00:10:50 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -S H,CrappyDell/Me,CrappyDell,02.08.2014 23:50,file:///C:/Users/Me/AppData/Roaming/OpenOffice/4; \ No newline at end of file diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/GabePatternSrcHolder.js --- a/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/GabePatternSrcHolder.js Thu Aug 07 00:10:50 2014 -0700 +++ b/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/GabePatternSrcHolder.js Sat Aug 09 03:34:26 2014 -0700 @@ -9,409 +9,15 @@ var modifier = require('./POPSyntaxGraphModifier'); var syntaxGraph = require('./buildGabePatternSyntaxGraph'); -var theObjColl = syntaxGraph.rootElem.__proto__.__proto__; -function ConstructorBuffer(){ -} +var persistence = require('./persistence'); -function ShadowGraphElem() { - this.portsInShadows = []; - this.portsOutShadows = []; - this.portsIn = []; - this.portsOut = []; - this.linkedElems = []; - this.viewSet = {}; -}; -var shadowGraphElemProto = new ConstructorBuffer(); -shadowGraphElemProto.__proto__ = theObjColl; -ShadowGraphElem.prototype = shadowGraphElemProto; -ShadowGraphElem.prototype.constructor = ShadowGraphElem; +//persistence.clearPersistentGraph(); +//persistence.persistTheGraph( syntaxGraph ); +//console.log("\npersisting graph done!\n") -//================================================== -//== -//================================================== - -//The src holder has to serialize the syntax graph out to persistent -// storage, and bring it back in, converting back to javascript objects -//JSON can stringify and then parse back, but it can't handle circuits -// in the graph, so have to write code that crawls the graph and -// individually stringifies each element -//So, the steps: -//-] crawl only the elements. -//-] An element has properties, ports, and linked elements attached.. -//- -] properties are fine as-is, the stringify will handle them -//- -] ports have pointers back to the element, so those must be replaced -// with the element's ID before stringify -//- -] linked elements can potentially link back around in a -// circle, so replace those links with IDs before stringify -// -//-] start at root, get the first element. -//-] For a given element: -//- -] check whether marked as already visited if yes, return, else -// continue -//- -] mark the element as having been visited -//- -] visit each port (save this position before visiting!), and -// for a given port: -//- - -] follow each pairedPort link, and process element linked to the -// paired port -//- - -] when come back, replace pairedPort pointer with ID of the -// pointed-to port -//- -] for each element in the array of linked elements: -//- - -] visit and process the element linked to (save position first) -//- - -] when come back, replace the pointer with ID of the linked elem -//- -] when all the elment's ports and linked elems have been visited, -// return to the position were in just before visiting this element -//-] when no where to return to, then done! -var persistTheGraph = function( theGraphRoot ) { - var shadowGraphRoot = {}; - - startPersisting(); //send notice to web server that persist protocol is starting - //save out the top level object that has the handles to the view hierarchy root - // and the root element. Also persist the view hierarchy root because it doesn't - // fit cleanly to try to reach it from the root element.. by persisting it here, - // can then do a single recursive call and only pass the next syntax element to - // clean up. Any view stuff will be reached from that element - var rootElem = theGraphRoot.rootElem; //save 'cause pointers about to be replaced - persistAnchorAndViewSetRoot( theGraphRoot, shadowGraphRoot ); - visitNextElemAndPersistIt( rootElem ); -// endPersisting(); //tell web server that persist protocol ended - - //now, restore all the pointers, replacing the IDs with pointer to object - restoreAnchorAndViewSetRoot( theGraphRoot, shadowGraphRoot ); - visitNextElemAndRestoreIt( rootElem ); -} - -function persistAnchorAndViewSetRoot( theGraphRoot, shadowGraphRoot ) { - //save the top level root object and the top view set, which is above - // the view set of the root element - //Replace pointer to root elem with its ID and cut off pointers - // inside the root view set link, so JSON stringify only gets the root - // view set and its one view set link object - shadowGraphRoot.rootElem = theGraphRoot.rootElem; - theGraphRoot.rootElem = theGraphRoot.rootElem.ID; - - //remove the back link from the view set link back to the root view set - shadowGraphRoot.backLinkToRootViewSet = theGraphRoot.rootViewSet.viewSetLinks[0].referenceViewSet; - theGraphRoot.rootViewSet.viewSetLinks[0].referenceViewSet = - theGraphRoot.rootViewSet.viewSetLinks[0].referenceViewSet.ID; - - //cut off the view set link, that points to the view set of the root element - shadowGraphRoot.linkToRootElemsViewSet = - theGraphRoot.rootViewSet.viewSetLinks[0].subordinateViewSet; - theGraphRoot.rootViewSet.viewSetLinks[0].subordinateViewSet = - theGraphRoot.rootViewSet.viewSetLinks[0].subordinateViewSet.ID; - - //Now, ready to go, stringify away! - var stringOfGraphRoot = JSON.stringify( theGraphRoot, null, '\t' ); - console.log("JSON of graphRoot: " + stringOfGraphRoot ); - persistString( stringOfGraphRoot ) -} - -//Contract: have already verified that this elem has not been visited before calling -var visitNextElemAndPersistIt = function( elem ) { - //mark the element as having been visited - elem.isAlreadyVisited = true; - - //cut the back link going from the view set to the elem node - if((elem.viewSet||{}).syntaxElem) { - elem.viewSet.syntaxElem = elem.viewSet.syntaxElem.ID; - } - - //visit each port - var elemToVisit = {}; var i = 0; var j = 0; var inPort = {}; - var ports = elem.portsIn; var numPorts = ports.length; var numPairedPorts = 0; - for( j = 0; j < numPorts; j++ ) { - inPort = ports[j]; - numPairedPorts = inPort.pairedPorts.length; - //follow each pairedPort link - for( i = 0; i < numPairedPorts; i++ ) { - //process element linked to the paired port - elemToVisit = inPort.pairedPorts[i].element; - if( !elemToVisit.isAlreadyVisited ) { - visitNextElemAndPersistIt( elemToVisit ); - } - //back from visit, replace pointer with ID of pointed to port - inPort.pairedPorts[i] = inPort.pairedPorts[i].ID; - } - //replace the element back-pointer in the port object - inPort.element = inPort.element.ID; - } - ports = elem.portsOut; numPorts = ports.length; var outPort = {}; - for( j = 0; j < numPorts; j++ ) { - outPort = ports[j]; - numPairedPorts = outPort.pairedPorts.length; - //follow each pairedPort link - for( i = 0; i < numPairedPorts; i++ ) { - //process element linked to the paired port - elemToVisit = outPort.pairedPorts[i].element; - if( !elemToVisit.isAlreadyVisited ) { - visitNextElemAndPersistIt( elemToVisit ); - } - //when come back, replace pointer with ID of pointed to port - outPort.pairedPorts[i] = outPort.pairedPorts[i].ID; - } - //replace the element back-pointer in the port object - outPort.element = outPort.element.ID; - } - var numLinkedElems = elem.linkedElems.length; - for( i = 0; i < numLinkedElems; i++ ) { - if( !(elem.linkedElems[i].isAlreadyVisited) ) { - visitNextElemAndPersistIt( elem.linkedElems[i] ); - } - //back from visit, replace pointer with ID of pointed to elem - elem.linkedElems[i] = elem.linkedElems[i].ID; - } - - //cut links to the view sets embedded within view set link objects - if((elem.viewSet||{}).viewSetLinks) { - var viewSet = elem.viewSet; - var numLinked = viewSet.viewSetLinks.length; - var viewSetLink = {}; - for (i = 0; i < numLinked; i++) { - viewSetLink = viewSet.viewSetLinks[i]; - if( viewSetLink ) { - viewSetLink.referenceViewSet = viewSetLink.referenceViewSet.ID; - viewSetLink.subordinateViewSet = viewSetLink.subordinateViewSet.ID; - } - } - } - //cut back-links to parent view boxes within view set tree - if((elem.viewSet||{}).rootViewBox ) { - walkViewTree( elem.viewSet.rootViewBox ); - } - - //this elem, and all objects reachable from it are now safe to be - // stringified with JSON.. so do it! - var stringOfElemNode = JSON.stringify( elem, null, '\t' ); - console.log("JSON of elem: " + stringOfElemNode ); - persistString( stringOfElemNode ); -} - -//now that the graph, with its view sets, has been written out to JSON, -// go back and restore the pointers, replacing the IDs with actual pointer. -//Replace top pointers from a shadow, then after this can lookup pointers by ID -function restoreAnchorAndViewSetRoot( theGraphRoot, shadowGraphRoot ) { - //just copy the above code and reverse the direction of the assignments! - theGraphRoot.rootElem = shadowGraphRoot.rootElem; - - //remove the back link from the view set link back to the root view set - theGraphRoot.rootViewSet.viewSetLinks[0].referenceViewSet = shadowGraphRoot.backLinkToRootViewSet; - - //cut off the view set link, that points to the view set of the root element - theGraphRoot.rootViewSet.viewSetLinks[0].subordinateViewSet = shadowGraphRoot.linkToRootElemsViewSet; -} - -//Contract: have already verified that this elem has not been visited before calling -var visitNextElemAndRestoreIt = function( elem ) { - //mark the element as having been visited - elem.isAlreadyVisited = false; //all start at true, making false marks as restored - - //restore the back link going from the view set to the elem node - if((elem.viewSet||{}).syntaxElem) { //idiom makes safe when viewset undefined - elem.viewSet.syntaxElem = elem.getByID(elem.viewSet.syntaxElem); - } - - //restore each port - var elemToVisit = {}; var i = 0; var j = 0; var inPort = {}; - var portsIn = elem.portsIn; var numPorts = portsIn.length; var numPairedPorts = 0; - //as go along, restore pointers by looking them up - for( j = 0; j < numPorts; j++ ) { - inPort = portsIn[j]; - - //restore the element back-pointer in the port object - inPort.element = inPort.getByID(inPort.element); - - //follow each pairedPort link - numPairedPorts = inPort.pairedPorts.length; - for( i = 0; i < numPairedPorts; i++ ) { - //process element linked to the paired port - //first, replace pointer to port - inPort.pairedPorts[i] = inPort.getByID(inPort.pairedPorts[i]); - elemToVisit = inPort.pairedPorts[i].element; - if( typeof elemToVisit == 'number' ) { - //means the element back-pointer inside the port on the other end - // is still the ID.. IE, that port and its element not restored yet - visitNextElemAndRestoreIt( outPort.getByID(elemToVisit) ); - } - else if( elemToVisit.isAlreadyVisited ) { //if still marked from before - //don't think this case will ever come up! if elem not restored, - // then the elem's port's back pointer will still be an ID and above - // if() will catch it.. - visitNextElemAndRestoreIt( elemToVisit ); - } - } - } - var outPorts = elem.portsOut; numPorts = outPorts.length; var outPort = {}; - for( j = 0; j < numPorts; j++ ) { - outPort = outPorts[j]; - //restore back link from outPort to its element - outPort.element = outPort.getByID(outPort.element); - numPairedPorts = outPort.pairedPorts.length; - //visit each pairedPort link - for( i = 0; i < numPairedPorts; i++ ) { - //restore the pointer to the paired port - outPort.pairedPorts[i] = outPort.getByID(outPort.pairedPorts[i]); - //process element linked to the paired port - elemToVisit = outPort.pairedPorts[i].element; - if( typeof elemToVisit == 'number' ) { - //means the element back-pointer inside the port on the other end - // is still the ID.. IE, that port and its element not restored yet - visitNextElemAndRestoreIt( outPort.getByID(elemToVisit) ); - } - else if( elemToVisit.isAlreadyVisited ) { //if needs restoring - //don't think this case will ever come up! if not restored, - // then the elem back pointer will still be an ID and above - // if() will catch it.. - visitNextElemAndRestoreIt( elemToVisit ); - } - } - } - var numLinkedElems = elem.linkedElems.length; - for( i = 0; i < numLinkedElems; i++ ) { - //restore linked elem first, then process that restored elem - elem.linkedElems[i] = elem.getByID( elem.linkedElems[i] ); - if( elem.linkedElems[i].isAlreadyVisited ) { - visitNextElemAndRestoreIt( elem.linkedElems[i] ); - } - } - - //restore links to the view sets embedded within view set link objects - if( (elem.viewSet||{}).viewSetLinks ) { - var viewSet = elem.viewSet; - var numLinked = viewSet.viewSetLinks.length; //array always exists - var viewSetLink = {}; - for (i = 0; i < numLinked; i++) { - viewSetLink = viewSet.viewSetLinks[i]; - if (viewSetLink) { - viewSetLink.referenceViewSet = elem.getByID(viewSetLink.referenceViewSet); - viewSetLink.subordinateViewSet = elem.getByID(viewSetLink.subordinateViewSet); - } - } - } - //restore back-links to parent view boxes within view set tree - if( (elem.viewSet||{}).rootViewBox ) { //idiom that's safe when viewSet undefine - walkViewTreeAndRestore( elem.viewSet.rootViewBox ); - } -} - -persistTheGraph( syntaxGraph ); - -console.log("\npersisting graph done!\n") - -//retrievePersistedString(); - -//walk the tree, replacing all parent pointers with ID -// dont bother keeping shadow copy, easily restored without copy -function walkViewTree( viewBox ) { - if(viewBox.parent) viewBox.parent = viewBox.parent.ID; - if(viewBox.children.length > 0) { - var childBox = {}; - var numChildren = viewBox.children.length; - for (i = 0; i < numChildren; i++) { - childBox = viewBox.children[i]; - if (childBox) { - walkViewTree( childBox ); - } - } - } -// persistString( JSON.stringify(viewBox)) - console.log("done recursing viewBox: " + viewBox.ID) -} -//walk the tree, restoring all IDs with parent and children pointers -function walkViewTreeAndRestore( viewBox ) { - //replace ID that's in the parent field with looked up object pointer - if(viewBox.parent) viewBox.parent = viewBox.getByID(viewBox.parent); - if(viewBox.children.length > 0) { //all view boxes should have array! - var childBox = {}; - var numChildren = viewBox.children.length; - for (i = 0; i < numChildren; i++) { - viewBox.children[i] = viewBox.getByID(viewBox.children[i]); - childBox = viewBox.children[i]; - if( childBox ) { - walkViewTree( childBox ); - } - } - } - console.log("done restoring viewBox: " + viewBox.ID) -} - -function startPersisting() { - //bottle server - var theUrl = "http://localhost:8080/startsavinggraph"; - var xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", theUrl, false ); - console.log("about to send start persisting command"); - xmlHttp.send( null ); - console.log("started persisting: " + xmlHttp.responseText ); -} - -function persistString( stringToWrite ) { - //bottle server - //convert newline to %N, tab to %T, backslash to %H and forward slash to %S - // then convert back in server before storing to file - stringToWrite = stringToWrite.replace(/[\t]/g,'%T'); - stringToWrite = stringToWrite.replace(/[\n]/g,'%N'); - stringToWrite = stringToWrite.replace(/[\\]/g,'%H'); - stringToWrite = stringToWrite.replace(/[/]/g,'%S'); - var theUrl = "http://localhost:8080/save1elem/" + stringToWrite; - - console.log("theURL: " + theUrl); - - var xmlHttp = null; - xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", theUrl, true ); - xmlHttp.onload = function (e) { - if (xmlHttp.readyState === 4) { - if (xmlHttp.status === 200) { - console.log("response to the write: " + xmlHttp.responseText ); - } else { - console.error(xmlHttp.statusText); - } - } - }; - xmlHttp.onerror = function (e) { - console.error(xmlHttp.statusText); - }; - xmlHttp.send( null ); -} - -function endPersisting() { - //bottle server - var theUrl = "http://localhost:8080/endsavinggraph"; - var xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", theUrl, false ); - xmlHttp.send( null ); - console.log("end persisting: " + xmlHttp.responseText ); -} - -//================================ - -function startRetrieve() { - var theUrl = "http://localhost:8080/startretrievinggraph"; - var xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", theUrl, false ); - xmlHttp.send( null ); - console.log("started retrieve: " + xmlHttp.responseText ); -} - -function retrieveNextPersistedString() { - var theUrl = "http://localhost:8080/get1elem"; - var xmlHttp = null; - - xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", theUrl, false ); - xmlHttp.send( null ); - console.log("retrieved string: " + xmlHttp.responseText ); -} - -function endRetrieve() { - var theUrl = "http://localhost:8080/endretrievinggraph"; - var xmlHttp = new XMLHttpRequest(); - xmlHttp.open( "GET", theUrl, false ); - xmlHttp.send( null ); - console.log("end retrieve: " + xmlHttp.responseText ); -} - -//================================= +//wait until all the writes complete before retrieving! +syntaxGraph = persistence.retrieveTheGraph(); +console.log("\nretrieving graph done!\n") return{ visualizer: visualizer, diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/POPDisplay.js --- a/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/POPDisplay.js Thu Aug 07 00:10:50 2014 -0700 +++ b/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/POPDisplay.js Sat Aug 09 03:34:26 2014 -0700 @@ -49,7 +49,7 @@ function acceptRootViewSet (rootViewSet) { //Here, convert each view hierarchy element into an equivalent // famous render tree node - +return; //during testing, log some known positions within the hierarchy console.log("POPDisplay: root view set: " + rootViewSet.ID); @@ -86,7 +86,7 @@ var viewBox = {}; var newSurface = {}; var newSurfMod = {}; var newContainer = {}; var newContMod = {}; var i = 0; var numChildren = 0; -rewrite display to handle view sets! +//rewrite display to handle view sets! var nextGenParents = []; var parentContainer = {}; var viewBoxChildren = []; nextGenParents.push( {viewBox: rootViewSet, container: rootContainer}); //loop, getting oldest parent pair in queue each time diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/POPGraphClasses.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/POPGraphClasses.js Sat Aug 09 03:34:26 2014 -0700 @@ -0,0 +1,167 @@ + + +define( function(require, exports, module) { + +//Using inheritance via prototype, to keep a running hash of objects +// indexed by their ID -- ID is auto generated as the objects are created +function ObjColl(){ + //start at 1 because 0 is interpreted same a null and other special ways + currID = 1; //private, will create one ObjColl and use as prototype + objColl = []; + this.getNextID = function( obj ) { + objColl[currID] = obj; //objColl and currID are in the closure! + return currID++; + }; + this.getByID = function( ID ) { + return objColl[ID]; + } + this.insertByID = function( obj ){ + objColl[obj.ID] = obj; //objColl is in the closure! + } +} +var theObjColl = new ObjColl(); + +//need all the classes to have common ObjColl instance, but each has +// different constructor! So, each needs its own prototype instance, so +// need to create an empty object to act as the prototype instance, so can +// add the constructor to that instance +function ConstructorBuffer(){ +} + +//create a "class" of graph elements.. make an elem via the "new" call.. +function GraphElem() { + this.type = 'GraphElem', + this.ID = this.getNextID(this); //this.getNextID promotes to prototype + this.properties = []; + this.portsIn = []; + this.portsOut = []; + this.linkedElems = []; + this.viewSet = undefined; +}; +var graphElemProto = new ConstructorBuffer(); +graphElemProto.__proto__ = theObjColl; +GraphElem.prototype = graphElemProto; +GraphElem.prototype.constructor = GraphElem; + +function ViewSet() { + this.type = 'ViewSet', + this.ID = this.getNextID(this); //this.getNextID promotes to prototype + this.syntaxElem = {}; //back link to the corresponding syntax graph element + this.rootViewBox = []; + this.viewSetLinks = []; +} +var viewSetProto = new ConstructorBuffer(); +viewSetProto.__proto__ = theObjColl; +ViewSet.prototype = viewSetProto; +ViewSet.prototype.constructor = ViewSet; + +function ViewSetLink() { + this.type = 'ViewSetLink', + this.ID = this.getNextID(this); //this.getNextID promotes to prototype + this.referenceViewSet = {}; + this.subordinateViewSet = {}; + this.xOffset = 0; + this.yOffset = 0; + this.scale = 1.0; +} +var viewSetLinkProto = new ConstructorBuffer(); +viewSetLinkProto.__proto__ = theObjColl; +ViewSetLink.prototype = viewSetLinkProto; +ViewSetLink.prototype.constructor = ViewSetLink; + +//the constructor for a ViewBox object +function ViewBox() { + this.type = 'ViewBox', + this.ID = this.getNextID(this); //this.getNextID promotes to prototype + this.shape = undefined; + this.width = 0; //size of bounding box (before scaling) + this.height = 0; + this.xOffset = 0; //offset moves self and all descendants rel to parent + this.yOffset = 0; + this.scale = 1.0; //scale applies to self and all descendants + this.parent = undefined; //allows traversing upward through hierarchy + this.children = []; //these are children view bounding boxes + this.handlers = []; //array of objects -> { typeOfEvent, Fn } +} +var viewBoxProto = new ConstructorBuffer(); +viewBoxProto.__proto__ = theObjColl; +ViewBox.prototype = viewBoxProto; +ViewBox.prototype.constructor = ViewBox; +ViewBox.prototype.WithParams = function(shape, width, height, xOffset, yOffset, scale) { + this.shape = shape; + this.width = width; //size of bounding box (before scaling) + this.height = height; + this.xOffset = xOffset; //offset moves self and all descendants rel to parent + this.yOffset = yOffset; + this.scale = scale; //scale applies to self and all descendants + return this; //so object returns from "constructor" call.. +} + +function GraphProperty() { + this.type = 'GraphProperty', + this.ID = this.getNextID(this); //this.getNextID promotes to prototype + this.propertyName = ""; + this.propertyValue = ""; + this.subProperties = []; +} +var graphPropertyProto = new ConstructorBuffer(); +graphPropertyProto.__proto__ = theObjColl; +GraphProperty.prototype = graphPropertyProto; +GraphProperty.prototype.constructor = GraphProperty; +GraphProperty.prototype.WithParams = function(propertyName, propertyValue) { + this.propertyName = propertyName; + this.propertyValue = propertyValue; + return this; +} + +function GraphPort() { + this.type = 'GraphPort', + this.ID = this.getNextID(this); //this.getNextID promotes to prototype + this.element = {}; + this.properties = []; + this.pairedPorts = []; +} +var graphPortProto = new ConstructorBuffer(); +graphPortProto.__proto__ = theObjColl; +GraphPort.prototype = graphPortProto; +GraphPort.prototype.constructor = GraphPort; +GraphPort.prototype.WithElem = function(elem) { + this.element = elem; + return this; +} + + +function stdKeyHdlr(e) { + console.log("key event: " + e.type + " on: " + e.target.ID); +} + +function stdClickHdlr(e) { + console.log("click event: " + e.type + " on: " + e.target.ID); +} + +function stdDragHdlr(e) { + console.log("drag event: " + e.type + " on: " + e.target.ID); +} + + +return { + theObjColl: theObjColl, + ObjColl: ObjColl, //the class that theObjColl is instance of + GraphElem: GraphElem, + graphElemProto: graphElemProto, + ViewSet: ViewSet, + viewSetProto: viewSetProto, + ViewSetLink: ViewSetLink, + viewSetLinkProto: viewSetLinkProto, + ViewBox: ViewBox, + viewBoxProto: viewBoxProto, + GraphProperty: GraphProperty, + graphPropertyProto: graphPropertyProto, + GraphPort: GraphPort, + graphPortProto: graphPortProto, + stdKeyHdlr: stdKeyHdlr, + stdClickHdlr: stdClickHdlr, + stdDragHdlr: stdDragHdlr +}; +}); + diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/persistence.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/app/persistence.js Sat Aug 09 03:34:26 2014 -0700 @@ -0,0 +1,646 @@ + +define( function(require, exports, module) { + +//Get the class objects that define behavior for each of the structures that +// appears in a POP syntax graph.. will use these as read in JSON of a graph +// and parse it into objects of these classes! +var graphClasses = require("./POPGraphClasses"); + + +//=========================== +//== Learning stuff -- playing with local file system things.. +//=========================== + +//note: only Chrome supports the file api ! +// start Chrome with flag "--allow-file-access-from-files" to enable the API + + +//put a file selector button onto the screen and register an event handler +// that fires after one or a group of files is chosen + var outputEl = document.body.appendChild(document.createElement("output")); + outputEl.id = "fileListOutput"; + + var inputEl = document.createElement("div"); + document.body.appendChild(inputEl); + var innerHTMLStr = ''; + inputEl.innerHTML = innerHTMLStr; + + var gottenElem = document.getElementById("fileChooserElem"); + gottenElem.addEventListener('change', handleFileSelect, false); + + //The event handler + function handleFileSelect(evt) { + var files = evt.target.files; // FileList object + + console.log("got event! "); + + // files is a FileList of File objects. List some properties. + var output = []; + for (var i = 0, f; f = files[i]; i++) { + output.push ( + '
  • ' + f.name + ' (' + (f.type || "n/a") + ') - ' + + f.size + ' bytes, last modified: ' + + (f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : "n/a") + + '
  • '); + var reader = new FileReader(); + + //The file API is asynchronous, so before do a read, register + // a callback that runs when the read completes. + // It's a Closure, which encloses the file info as theFile. + //The closure runs now, which makes a function invocation body + // that includes the parameter, and this run makes a new function + // spec which is returned as the callback! That new function spec + // has the invocation of the closure attached to it, so it still + // has the closure parameter (theFile) available when it is called + // back later. + reader.onload = (function(theFile) { + return function(e) { + console.log("file: " + theFile.name + " content: " + e.target.result); + }; + })(f); //here, invoke the closure function, passing it the file + + // trigger the onload -- reads in the image file as a data URL. + console.log("file contents: " + reader.readAsText(f) ); + } + document.getElementById('fileListOutput').innerHTML = ''; + console.log("files: " + output.join("")); + } + +//this is a call-back that runs at the conclusion of initializing the file system + function onInitFs(fs) { + console.log('\n\n!!!Opened file system: ' + fs.name +"\n\n"); + } + + +//note, in order to get persistent file system access to work, had to start +// chrome with the flag "--allow-file-access-from-files" and added +// "--allow-file-access" just for good measure +//Test that file system access works via the Filer.js demo in the packages- +// libraries-tools directory -- click on index and see if red "error" shows up + navigator.webkitPersistentStorage.requestQuota ( + 1024*1024*280, + function(grantedBytes) { + document.getElementById('fileListOutput').innerHTML = 'request quota callback: ' + grantedBytes; + window.webkitRequestFileSystem(window.PERSISTENT, grantedBytes, onInitFs, errorHandler); + }, + errorHandler + ) + + + function errorHandler(e) { + var msg = ''; + + switch (e.code) { + case FileError.QUOTA_EXCEEDED_ERR: + msg = 'QUOTA_EXCEEDED_ERR'; + break; + case FileError.NOT_FOUND_ERR: + msg = 'NOT_FOUND_ERR'; + break; + case FileError.SECURITY_ERR: + msg = 'SECURITY_ERR'; + break; + case FileError.INVALID_MODIFICATION_ERR: + msg = 'INVALID_MODIFICATION_ERR'; + break; + case FileError.INVALID_STATE_ERR: + msg = 'INVALID_STATE_ERR'; + break; + default: + msg = 'Unknown Error'; + break; + }; + + console.log('Error: ' + msg); + } + + +//================================================== +//== +//================================================== + +//The src holder has to serialize the syntax graph out to persistent +// storage, and bring it back in, converting back to javascript objects +//JSON can stringify and then parse back, but it can't handle circuits +// in the graph, so have to write code that crawls the graph and +// individually stringifies each element +//So, the steps: +//-] crawl only the elements. +//-] An element has properties, ports, and linked elements attached.. +//- -] properties are fine as-is, the stringify will handle them +//- -] ports have pointers back to the element, so those must be replaced +// with the element's ID before stringify +//- -] linked elements can potentially link back around in a +// circle, so replace those links with IDs before stringify +// +//-] start at root, get the first element. +//-] For a given element: +//- -] check whether marked as already visited if yes, return, else +// continue +//- -] mark the element as having been visited +//- -] visit each port (save this position before visiting!), and +// for a given port: +//- - -] follow each pairedPort link, and process element linked to the +// paired port +//- - -] when come back, replace pairedPort pointer with ID of the +// pointed-to port +//- -] for each element in the array of linked elements: +//- - -] visit and process the element linked to (save position first) +//- - -] when come back, replace the pointer with ID of the linked elem +//- -] when all the elment's ports and linked elems have been visited, +// return to the position were in just before visiting this element +//-] when no where to return to, then done! + function persistTheGraph( theGraphRoot ) { + var shadowGraphRoot = {}; + + //save out the top level object that has the handles to the view hierarchy root + // and the root element. Also persist the view hierarchy root because it doesn't + // fit cleanly to try to reach it from the root element.. by persisting it here, + // can then do a single recursive call and only pass the next syntax element to + // clean up. Any view stuff will be reached from that element + var rootElem = theGraphAnchor.rootElem; //save 'cause pointers about to be replaced + persistAnchorAndViewSetRoot(theGraphAnchor, shadowGraphRoot); + visitNextElemAndPersistIt(rootElem); +// endPersisting(); //tell web server that persist protocol ended + + //now, restore all the pointers, replacing the IDs with pointer to object + restoreAnchorAndViewSetRoot(theGraphAnchor, shadowGraphRoot); + visitNextElemAndRestoreIt(rootElem); + } + + function persistAnchorAndViewSetRoot(theGraphRoot, shadowGraphRoot) { + //save the top level root object and the top view set, which is above + // the view set of the root element + //Replace pointer to root elem with its ID and cut off pointers + // inside the root view set link, so JSON stringify only gets the root + // view set and its one view set link object + shadowGraphRoot.rootElem = theGraphAnchor.rootElem; + theGraphAnchor.rootElem = theGraphAnchor.rootElem.ID; + + //remove the back link from the view set link back to the root view set + shadowGraphRoot.backLinkToRootViewSet = theGraphAnchor.rootViewSet.viewSetLinks[0].referenceViewSet; + theGraphAnchor.rootViewSet.viewSetLinks[0].referenceViewSet = + theGraphAnchor.rootViewSet.viewSetLinks[0].referenceViewSet.ID; + + //cut off the view set link, that points to the view set of the root element + shadowGraphRoot.linkToRootElemsViewSet = + theGraphAnchor.rootViewSet.viewSetLinks[0].subordinateViewSet; + theGraphAnchor.rootViewSet.viewSetLinks[0].subordinateViewSet = + theGraphAnchor.rootViewSet.viewSetLinks[0].subordinateViewSet.ID; + + //Now, ready to go, stringify away! + var stringOfGraphRoot = JSON.stringify(theGraphAnchor, null, '\t'); +// console.log("JSON of graphRoot: " + stringOfGraphRoot ); + persistString(stringOfGraphRoot) + } + +//Contract: have already verified that this elem has not been visited before calling + function visitNextElemAndPersistIt(elem) { + //mark the element as having been visited + elem.isAlreadyVisited = true; + + //cut the back link going from the view set to the elem node + if ((elem.viewSet || {}).syntaxElem) { + elem.viewSet.syntaxElem = elem.viewSet.syntaxElem.ID; + } + + //visit each port + var elemToVisit = {}; + var i = 0; + var j = 0; + var inPort = {}; + var ports = elem.portsIn; + var numPorts = ports.length; + var numPairedPorts = 0; + for (j = 0; j < numPorts; j++) { + inPort = ports[j]; + numPairedPorts = inPort.pairedPorts.length; + //follow each pairedPort link + for (i = 0; i < numPairedPorts; i++) { + //process element linked to the paired port + elemToVisit = inPort.pairedPorts[i].element; + if (!elemToVisit.isAlreadyVisited) { + visitNextElemAndPersistIt(elemToVisit); + } + //back from visit, replace pointer with ID of pointed to port + inPort.pairedPorts[i] = inPort.pairedPorts[i].ID; + } + //replace the element back-pointer in the port object + inPort.element = inPort.element.ID; + } + ports = elem.portsOut; + numPorts = ports.length; + var outPort = {}; + for (j = 0; j < numPorts; j++) { + outPort = ports[j]; + numPairedPorts = outPort.pairedPorts.length; + //follow each pairedPort link + for (i = 0; i < numPairedPorts; i++) { + //process element linked to the paired port + elemToVisit = outPort.pairedPorts[i].element; + if (!elemToVisit.isAlreadyVisited) { + visitNextElemAndPersistIt(elemToVisit); + } + //when come back, replace pointer with ID of pointed to port + outPort.pairedPorts[i] = outPort.pairedPorts[i].ID; + } + //replace the element back-pointer in the port object + outPort.element = outPort.element.ID; + } + var numLinkedElems = elem.linkedElems.length; + for (i = 0; i < numLinkedElems; i++) { + if (!(elem.linkedElems[i].isAlreadyVisited)) { + visitNextElemAndPersistIt(elem.linkedElems[i]); + } + //back from visit, replace pointer with ID of pointed to elem + elem.linkedElems[i] = elem.linkedElems[i].ID; + } + + //cut links to the view sets embedded within view set link objects + if ((elem.viewSet || {}).viewSetLinks) { + var viewSet = elem.viewSet; + var numLinked = viewSet.viewSetLinks.length; + var viewSetLink = {}; + for (i = 0; i < numLinked; i++) { + viewSetLink = viewSet.viewSetLinks[i]; + if (viewSetLink) { + viewSetLink.referenceViewSet = viewSetLink.referenceViewSet.ID; + viewSetLink.subordinateViewSet = viewSetLink.subordinateViewSet.ID; + } + } + } + //cut back-links to parent view boxes within view set tree + if ((elem.viewSet || {}).rootViewBox) { + walkViewTree(elem.viewSet.rootViewBox); + } + + //this elem, and all objects reachable from it are now safe to be + // stringified with JSON.. so do it! + var stringOfElemNode = JSON.stringify(elem, null, '\t'); +// console.log("JSON of elem: " + stringOfElemNode ); + persistString(stringOfElemNode); + } + +//walk the tree, replacing all parent pointers with ID +// dont bother keeping shadow copy, easily restored without copy + function walkViewTree(viewBox) { + if (viewBox.parent) viewBox.parent = viewBox.parent.ID; + if (viewBox.children.length > 0) { + var childBox = {}; + var numChildren = viewBox.children.length; + for (i = 0; i < numChildren; i++) { + childBox = viewBox.children[i]; + if (childBox) { + walkViewTree(childBox); + } + } + } +// console.log("done recursing viewBox: " + viewBox.ID) + } + + //===================================== +//Search the graph for places where pointer has been replaced by an ID, +// when find one, look up the object with that ID and replace the ID with pointer + function restoreAnchorAndViewSetRoot( theGraphAnchor ) { + var theObjColl = graphClasses.theObjColl; + + theGraphAnchor.rootElem = theObjColl.getByID(theGraphAnchor.rootElem); + + //restore the back link from the view set link + theGraphAnchor.rootViewSet.viewSetLinks[0].referenceViewSet = + theObjColl.getByID(theGraphAnchor.rootViewSet.viewSetLinks[0].referenceViewSet); + theGraphAnchor.rootViewSet.viewSetLinks[0].subordinateViewSet = + theObjColl.getByID(theGraphAnchor.rootViewSet.viewSetLinks[0].subordinateViewSet); + } + +//Contract: have already verified that this elem has not been visited before calling + function visitNextElemAndRestoreIt( elem ) { + //mark the element as having been visited + elem.isAlreadyVisited = false; //all start at true, making false marks as restored + + //restore the back link going from the view set to the elem node + if ((elem.viewSet || {}).syntaxElem) { //idiom makes safe when viewset undefined + elem.viewSet.syntaxElem = elem.getByID(elem.viewSet.syntaxElem); + } + + //restore each port + var elemToVisit = {}; + var i = 0; + var j = 0; + var inPort = {}; + var portsIn = elem.portsIn; + var numPorts = portsIn.length; + var numPairedPorts = 0; + //as go along, restore pointers by looking them up + for (j = 0; j < numPorts; j++) { + inPort = portsIn[j]; + + //restore the element back-pointer in the port object + inPort.element = inPort.getByID(inPort.element); + + //follow each pairedPort link + numPairedPorts = inPort.pairedPorts.length; + for (i = 0; i < numPairedPorts; i++) { + //process element linked to the paired port + //first, replace pointer to port + inPort.pairedPorts[i] = inPort.getByID(inPort.pairedPorts[i]); + elemToVisit = inPort.pairedPorts[i].element; + if (typeof elemToVisit == 'number') { + //means the element back-pointer inside the port on the other end + // is still the ID.. IE, that port and its element not restored yet + visitNextElemAndRestoreIt(outPort.getByID(elemToVisit)); + } + else if (elemToVisit.isAlreadyVisited) { //if still marked from before + //don't think this case will ever come up! if elem not restored, + // then the elem's port's back pointer will still be an ID and above + // if() will catch it.. + visitNextElemAndRestoreIt(elemToVisit); + } + } + } + var outPorts = elem.portsOut; + numPorts = outPorts.length; + var outPort = {}; + for (j = 0; j < numPorts; j++) { + outPort = outPorts[j]; + //restore back link from outPort to its element + outPort.element = outPort.getByID(outPort.element); + numPairedPorts = outPort.pairedPorts.length; + //visit each pairedPort link + for (i = 0; i < numPairedPorts; i++) { + //restore the pointer to the paired port + outPort.pairedPorts[i] = outPort.getByID(outPort.pairedPorts[i]); + //process element linked to the paired port + elemToVisit = outPort.pairedPorts[i].element; + if (typeof elemToVisit == 'number') { + //means the element back-pointer inside the port on the other end + // is still the ID.. IE, that port and its element not restored yet + visitNextElemAndRestoreIt(outPort.getByID(elemToVisit)); + } + else if (elemToVisit.isAlreadyVisited) { //if needs restoring + //don't think this case will ever come up! if not restored, + // then the elem back pointer will still be an ID and above + // if() will catch it.. + visitNextElemAndRestoreIt(elemToVisit); + } + } + } + var numLinkedElems = elem.linkedElems.length; + for (i = 0; i < numLinkedElems; i++) { + //restore linked elem first, then process that restored elem + elem.linkedElems[i] = elem.getByID(elem.linkedElems[i]); + if (elem.linkedElems[i].isAlreadyVisited) { + visitNextElemAndRestoreIt(elem.linkedElems[i]); + } + } + + //restore links to the view sets embedded within view set link objects + if ((elem.viewSet || {}).viewSetLinks) { + var viewSet = elem.viewSet; + var numLinked = viewSet.viewSetLinks.length; //array always exists + var viewSetLink = {}; + for (i = 0; i < numLinked; i++) { + viewSetLink = viewSet.viewSetLinks[i]; + if (viewSetLink) { + viewSetLink.referenceViewSet = elem.getByID(viewSetLink.referenceViewSet); + viewSetLink.subordinateViewSet = elem.getByID(viewSetLink.subordinateViewSet); + } + } + } + //restore back-links to parent view boxes within view set tree + if ((elem.viewSet || {}).rootViewBox) { //idiom that's safe when viewSet undefine + walkViewTreeAndRestore(elem.viewSet.rootViewBox); + } + } + +//walk the tree, restoring all IDs with parent and children pointers + function walkViewTreeAndRestore(viewBox) { + //replace ID that's in the parent field with looked up object pointer + if (viewBox.parent) viewBox.parent = viewBox.getByID(viewBox.parent); + if (viewBox.children.length > 0) { //all view boxes should have array! + var childBox = {}; + var numChildren = viewBox.children.length; + for (i = 0; i < numChildren; i++) { + viewBox.children[i] = viewBox.getByID(viewBox.children[i]); + childBox = viewBox.children[i]; + if (childBox) { + walkViewTree(childBox); + } + } + } +// console.log("done restoring viewBox: " + viewBox.ID) + } + +//================================== + + function clearPersistentGraph() { + //bottle server + var theUrl = "http://localhost:8080/cleargraph"; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false); + console.log("about to send clear graph command"); + xmlHttp.send(null); + console.log("done clearing: " + xmlHttp.responseText); + } + + function persistString(stringToWrite) { + //bottle server + //convert newline to %N, tab to %T, backslash to %H and forward slash to %S + // then convert back in server before storing to file + stringToWrite = stringToWrite.replace(/[\t]/g, '%T'); + stringToWrite = stringToWrite.replace(/[\n]/g, '%N'); + stringToWrite = stringToWrite.replace(/[\\]/g, '%H'); + stringToWrite = stringToWrite.replace(/[/]/g, '%S'); + var theUrl = "http://localhost:8080/save1elem/" + stringToWrite; + + console.log("theURL: " + theUrl); + + var xmlHttp = null; + xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false); //true means send asynchronously + xmlHttp.onload = function (e) { + if (xmlHttp.readyState === 4) { + if (xmlHttp.status === 200) { + console.log("response to the write: " + xmlHttp.responseText); + } else { + console.error(xmlHttp.statusText); + } + } + }; + xmlHttp.onerror = function (e) { + console.error(xmlHttp.statusText); + }; + xmlHttp.send(null); + } + + function endPersisting() { + //bottle server + var theUrl = "http://localhost:8080/endsavinggraph"; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false);//do synchronously + xmlHttp.send(null); + console.log("end persisting: " + xmlHttp.responseText); + } + + +//============================================================= +//== +//== Retrieve SyntaxGraph from server +//== +//============================================================= + + function retrieveTheGraph() { + var theUrl = "http://localhost:8080/retrievegraph"; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false); //do synchronously + xmlHttp.send(null); + var theJSONString = xmlHttp.responseText; + + //starting a new graph, so the set of objects in the obj coll not + // objects from this new graph -- reset the object collection + // then fill it back up again as parse the objects in.. + graphClasses.theObjColl = new graphClasses.ObjColl(); //make empty one + + console.log( "Retrieved: " + theJSONString ); + + //split the string into separate JSON object-strings + var objJSONArray = theJSONString.split(/separator\n/); + + //remove the last, empty string + var temp = objJSONArray.pop(); + if( temp !== "" ) objJSONArray.push(temp); //if not empty, put it back + + //now parse each string, turning it into objects, and registering them + for( var i = 0; i < objJSONArray.length; i++ ) { + JSON.parse(objJSONArray[i], restoreParsedObj); + } + //one of the parsed JSON strings was the graph anchor obj, which + // was placed into theObjColl during JSON parsing. Grab it + // then call restore, which will walk the graph, find the places + // where a pointer should be, grab the ID sitting there, and use + // that to get the obj and replace the ID with pointer to the object + theGraphAnchor = graphClasses.theObjColl.graphAnchor; + restoreAnchorAndViewSetRoot( theGraphAnchor ); + visitNextElemAndRestoreIt( theGraphAnchor.rootElem ); + return theGraphAnchor; + } + + //This registers each object that has an ID and restores any that have + // a type to their correct class, and it restores handler functions + function restoreParsedObj(k,v) { + if(v.rootElem) { //This is the graph anchor (AKA head)! + graphClasses.theObjColl.graphAnchor = v; + } + if(v.ID) { //ID is a valid field when v is a full object, to be registered + console.log("inserting: " + k + " | ID: " + v.ID); + graphClasses.theObjColl.insertByID( v ); //have a struct worthy of registering + } + if( v.type ) {//add the appropriate __proto__ obj + switch( v.type ) { + case "GraphElem": v.__proto__ = graphClasses.graphElemProto; + break; + case "ViewSet": v.__proto__ = graphClasses.viewSetProto; + break; + case "ViewSetLink": v.__proto__ = graphClasses.viewSetLinkProto; + break; + case "ViewBox": v.__proto__ = graphClasses.viewBoxProto; + break; + case "GraphProperty": v.__proto__ = graphClasses.graphPropertyProto; + break; + case "GraphPort": v.__proto__ = graphClasses.graphPortProto; + break; + } + } + switch(k) { + case "handlers": //view box's array of handler fns + var hdlrPair; //v is an array of objects, each w/type and fn + for( var i = 0; i < v.length; i++) { + hdlrPair = v[i]; + switch (hdlrPair.type) { + case "key": + hdlrPair.fn = graphClasses.stdKeyHdlr; + break; + case "click": + hdlrPair.fn = graphClasses.stdClickHdlr; + break; + case "drag": + hdlrPair.fn = graphClasses.stdDragHdlr; + break; + } + } + + //console.log("key: " + k + " | ret value: " + v + " add .toJSON before stringify!"); + break; + case "isAlreadyVisited": + return true; //restore relies on this being true + break; + } + return v; + } + +// //for this top group, if value is number then it's the ID of obj to get +// case "rootElem": +// case "referenceViewSet": +// case "subordinateViewSet": +// case "element": +// case "syntaxElem": +// case "parent": +// if( typeof v === 'number' ) { +// var theObj = graphClasses.theObjColl.getByID(v); +// var retValue = theObj ? theObj : v; //means need two passes! +// console.log("key: " + k + " | ID: " + v + " | ret value: " + retValue); +// return retValue; +// } +// break; +// //for these two, value is an array of IDs +// case "pairedPorts": +// case "linkedElems": +// for( var i = 0; i < v.length; i++) { +// var val = v[i]; +// if( typeof val === 'number' ) { +// var theObj = graphClasses.theObjColl.getByID(val); +// var retValue = theObj ? theObj : val; //means need two passes! +// console.log("array -- key: " + k + " | ID: " + v[i] + " | ret value: " + i + ": " + retValue); +// v[i] = retValue; +// } +// } +// return v; +// break; + +//================================ + function startRetrieve() { + var theUrl = "http://localhost:8080/startretrievinggraph"; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false); + xmlHttp.send(null); + console.log("started retrieve: " + xmlHttp.responseText); + } + + function retrieveNextPersistedString() { + var theUrl = "http://localhost:8080/get1elem"; + var xmlHttp = null; + + xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false); + xmlHttp.send(null); + console.log("retrieved string: " + xmlHttp.responseText); + } + + function endRetrieve() { + var theUrl = "http://localhost:8080/endretrievinggraph"; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", theUrl, false); + xmlHttp.send(null); + console.log("end retrieve: " + xmlHttp.responseText); + } + +//================================= + + +return { + persistString: persistString, + clearPersistentGraph: clearPersistentGraph, + persistTheGraph: persistTheGraph, + retrieveTheGraph: retrieveTheGraph +}; +}); diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/lib/filer.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1__Development/0__Code_Dev/Javascript_approach/MVDM_implementation/lib/filer.js Sat Aug 09 03:34:26 2014 -0700 @@ -0,0 +1,836 @@ +/** + * Copyright 2013 - Eric Bidelman + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + * @fileoverview + * Convenient wrapper library for the HTML5 Filesystem API, implementing + * familiar UNIX commands (cp, mv, ls) for its API. + * + * @author Eric Bidelman (ebidel@gmail.com) + * @version: 0.4.3 + */ + +'use strict'; + +var self = this; // window or worker context. + +self.URL = self.URL || self.webkitURL; +self.requestFileSystem = self.requestFileSystem || self.webkitRequestFileSystem; +self.resolveLocalFileSystemURL = self.resolveLocalFileSystemURL || + self.webkitResolveLocalFileSystemURL; +navigator.temporaryStorage = navigator.temporaryStorage || + navigator.webkitTemporaryStorage; +navigator.persistentStorage = navigator.persistentStorage || + navigator.webkitPersistentStorage; +self.BlobBuilder = self.BlobBuilder || self.MozBlobBuilder || + self.WebKitBlobBuilder; + +// Prevent errors in browsers that don't support FileError. +if (self.FileError === undefined) { + var FileError = function() {}; + FileError.prototype.prototype = Error.prototype; +} + +var Util = { + + /** + * Turns a NodeList into an array. + * + * @param {NodeList} list The array-like object. + * @return {Array} The NodeList as an array. + */ + toArray: function(list) { + return Array.prototype.slice.call(list || [], 0); + }, + + /*toDataURL: function(contentType, uint8Array) { + return 'data:' + contentType + ';base64,' + + self.btoa(this.arrayToBinaryString(uint8Array)); + },*/ + + /** + * Creates a data: URL from string data. + * + * @param {string} str The content to encode the data: URL from. + * @param {string} contentType The mimetype of the data str represents. + * @param {bool=} opt_isBinary Whether the string data is a binary string + * (and therefore should be base64 encoded). True by default. + * @return {string} The created data: URL. + */ + strToDataURL: function(str, contentType, opt_isBinary) { + var isBinary = opt_isBinary != undefined ? opt_isBinary : true; + if (isBinary) { + return 'data:' + contentType + ';base64,' + self.btoa(str); + } else { + return 'data:' + contentType + ',' + str; + } + }, + + /** + * Creates a blob: URL from a binary str. + * + * @param {string} binStr The content as a binary string. + * @param {string=} opt_contentType An optional mimetype of the data. + * @return {string} A new blob: URL. + */ + strToObjectURL: function(binStr, opt_contentType) { + + var ui8a = new Uint8Array(binStr.length); + for (var i = 0; i < ui8a.length; ++i) { + ui8a[i] = binStr.charCodeAt(i); + } + + var blob = new Blob([ui8a], + opt_contentType ? {type: opt_contentType} : {}); + + return self.URL.createObjectURL(blob); + }, + + /** + * Creates a blob: URL from a File or Blob object. + * + * @param {Blob|File} blob The File or Blob data. + * @return {string} A new blob: URL. + */ + fileToObjectURL: function(blob) { + return self.URL.createObjectURL(blob); + }, + + /** + * Reads a File or Blob object and returns it as an ArrayBuffer. + * + * @param {Blob|File} blob The File or Blob data. + * @param {Function} callback Success callback passed the array buffer. + * @param {Function=} opt_error Optional error callback if the read fails. + */ + fileToArrayBuffer: function(blob, callback, opt_errorCallback) { + var reader = new FileReader(); + reader.onload = function(e) { + callback(e.target.result); + }; + reader.onerror = function(e) { + if (opt_errorCallback) { + opt_errorCallback(e); + } + }; + + reader.readAsArrayBuffer(blob); + }, + + /** + * Creates and returns a blob from a data URL (either base64 encoded or not). + * + * @param {string} dataURL The data URL to convert. + * @return {Blob} A blob representing the array buffer data. + */ + dataURLToBlob: function(dataURL) { + var BASE64_MARKER = ';base64,'; + if (dataURL.indexOf(BASE64_MARKER) == -1) { + var parts = dataURL.split(','); + var contentType = parts[0].split(':')[1]; + var raw = decodeURIComponent(parts[1]); + + return new Blob([raw], {type: contentType}); + } + + var parts = dataURL.split(BASE64_MARKER); + var contentType = parts[0].split(':')[1]; + var raw = window.atob(parts[1]); + var rawLength = raw.length; + + var uInt8Array = new Uint8Array(rawLength); + + for (var i = 0; i < rawLength; ++i) { + uInt8Array[i] = raw.charCodeAt(i); + } + + return new Blob([uInt8Array], {type: contentType}); + }, + + /** + * Reads an ArrayBuffer as returns its contents as a binary string. + * + * @param {ArrayBuffer} buffer The buffer of data. + * @param {string=} opt_contentType An optional mimetype of the data. + * @return {Blob} A blob representing the array buffer data. + */ + arrayBufferToBlob: function(buffer, opt_contentType) { + var uInt8Array = new Uint8Array(buffer); + return new Blob([uInt8Array], + opt_contentType ? {type: opt_contentType} : {}); + }, + + /** + * Reads an ArrayBuffer as returns its contents as a binary string. + * + * @param {ArrayBuffer} buffer The buffer of data. + * @param {Function} callback Success callback passed the binary string. + * @param {Function=} opt_error Optional error callback if the read fails. + */ + arrayBufferToBinaryString: function(buffer, callback, opt_errorCallback) { + var reader = new FileReader(); + reader.onload = function(e) { + callback(e.target.result); + }; + reader.onerror = function(e) { + if (opt_errorCallback) { + opt_errorCallback(e); + } + }; + + var uInt8Array = new Uint8Array(buffer); + reader.readAsBinaryString(new Blob([uInt8Array])); + }, + + /** + * Create a binary string out of an array of numbers (bytes), each varying + * from 0-255. + * + * @param {Array} bytes The array of numbers to transform into a binary str. + * @return {string} The byte array as a string. + */ + arrayToBinaryString: function(bytes) { + if (typeof bytes != typeof []) { + return null; + } + var i = bytes.length; + var bstr = new Array(i); + while (i--) { + bstr[i] = String.fromCharCode(bytes[i]); + } + return bstr.join(''); + }, + + /** + * Returns the file extension for a given filename. + * + * @param {string} filename The filename. + * @return {string} The file's extension. + */ + getFileExtension: function(filename) { + var idx = filename.lastIndexOf('.'); + return idx != -1 ? filename.substring(idx) : ''; + } +}; + + +var MyFileError = function(obj) { + this.prototype = FileError.prototype; + this.code = obj.code; + this.name = obj.name; +}; +//MyFileError.prototype.__proto__ = FileError.prototype; + +// Extend FileError with custom errors and a convenience method to get error +// code mnemonic. +FileError.BROWSER_NOT_SUPPORTED = 1000; + +// TODO: remove when FileError.name is implemented (crbug.com/86014). +FileError.prototype.__defineGetter__('name', function() { + var keys = Object.keys(FileError); + for (var i = 0, key; key = keys[i]; ++i) { + if (FileError[key] == this.code) { + return key; + } + } + return 'Unknown Error'; +}); + + +var Filer = new function() { + + var FS_INIT_ERROR_MSG = 'Filesystem has not been initialized.'; + var NOT_IMPLEMENTED_MSG = 'Not implemented.'; + var NOT_A_DIRECTORY = 'Path was not a directory.'; + var INCORRECT_ARGS = 'These method arguments are not supported.'; + var FS_URL_SCHEME = 'filesystem:'; + var DEFAULT_FS_SIZE = 1024 * 1024; // 1MB. + + var fs_ = null; + var cwd_ = null; + var isOpen_ = false; + + var isFsURL_ = function(path) { + return path.indexOf(FS_URL_SCHEME) == 0; + }; + + // Path can be relative or absolute. If relative, it's taken from the cwd_. + // If a filesystem URL is passed it, it is simple returned + var pathToFsURL_ = function(path) { + if (!isFsURL_(path)) { + if (path[0] == '/') { + path = fs_.root.toURL() + path.substring(1); + } else if (path.indexOf('./') == 0 || path.indexOf('../') == 0) { + if (path == '../' && cwd_ != fs_.root) { + path = cwd_.toURL() + '/' + path; + } else { + path = cwd_.toURL() + path; + } + } else { + path = cwd_.toURL() + '/' + path; + } + } + + return path; + }; + + /** + * Looks up a FileEntry or DirectoryEntry for a given path. + * + * @param {function(...FileEntry|DirectorEntry)} callback A callback to be + * passed the entry/entries that were fetched. The ordering of the + * entries passed to the callback correspond to the same order passed + * to this method. + * @param {...string} var_args 1-2 paths to lookup and return entries for. + * These can be paths or filesystem: URLs. + */ + var getEntry_ = function(callback, var_args) { + var srcStr = arguments[1]; + var destStr = arguments[2]; + + var onError = function(e) { + if (e.code == FileError.NOT_FOUND_ERR) { + if (destStr) { + throw new Error('"' + srcStr + '" or "' + destStr + + '" does not exist.'); + } else { + throw new Error('"' + srcStr + '" does not exist.'); + } + } else { + throw new Error('Problem getting Entry for one or more paths.'); + } + }; + + // Build a filesystem: URL manually if we need to. + var src = pathToFsURL_(srcStr); + + if (arguments.length == 3) { + var dest = pathToFsURL_(destStr); + self.resolveLocalFileSystemURL(src, function(srcEntry) { + self.resolveLocalFileSystemURL(dest, function(destEntry) { + callback(srcEntry, destEntry); + }, onError); + }, onError); + } else { + self.resolveLocalFileSystemURL(src, callback, onError); + } + }; + + /** + * Copy or moves a file or directory to a destination. + * + * See public method's description (Filer.cp()) for rest of params. + * @param {Boolean=} opt_deleteOrig True if the original entry should be + * deleted after the copy takes place, essentially making the operation + * a move instead of a copy. Defaults to false. + */ + var copyOrMove_ = function(src, dest, opt_newName, opt_successCallback, + opt_errorHandler, opt_deleteOrig) { + var self = this; + + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + if (typeof src != typeof dest) { + throw new Error(INCORRECT_ARGS); + } + + var newName = opt_newName || null; + var deleteOrig = opt_deleteOrig != undefined ? opt_deleteOrig : false; + + if ((src.isFile || dest.isDirectory) && dest.isDirectory) { + if (deleteOrig) { + src.moveTo(dest, newName, opt_successCallback, opt_errorHandler); + } else { + src.copyTo(dest, newName, opt_successCallback, opt_errorHandler); + } + } else { + getEntry_(function(srcEntry, destDir) { + if (!destDir.isDirectory) { + var e = new Error('Oops! "' + destDir.name + ' is not a directory!'); + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + return; + } + if (deleteOrig) { + srcEntry.moveTo(destDir, newName, opt_successCallback, opt_errorHandler); + } else { + srcEntry.copyTo(destDir, newName, opt_successCallback, opt_errorHandler); + } + }, src, dest); + } + } + + function Filer(fs) { + fs_ = fs || null; + if (fs_) { + cwd_ = fs_.root; + isOpen_ = true; // TODO: this may not be the case. + } + } + + Filer.DEFAULT_FS_SIZE = DEFAULT_FS_SIZE; + Filer.version = '0.4.3'; + + Filer.prototype = { + get fs() { + return fs_; + }, + get isOpen() { + return isOpen_; + }, + get cwd() { + return cwd_; + } + } + + /** + * Constructs and returns a filesystem: URL given a path. + * + * @param {string=} path The path to construct a URL for. + * size {int=} The storage size (in bytes) to open the filesystem with. + * Defaults to DEFAULT_FS_SIZE. + * @return {string} The filesystem: URL. + */ + Filer.prototype.pathToFilesystemURL = function(path) { + return pathToFsURL_(path); + } + + /** + * Initializes (opens) the file system. + * + * @param {object=} opt_initObj Optional object literal with the following + * properties. Note: If {} or null is passed, default values are used. + * persistent {Boolean=} Whether the browser should use persistent quota. + * Default is false. + * size {int=} The storage size (in bytes) to open the filesystem with. + * Defaults to DEFAULT_FS_SIZE. + * @param {Function=} opt_successCallback Optional success handler passed a + * DOMFileSystem object. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.init = function(opt_initObj, opt_successCallback, + opt_errorHandler) { + if (!self.requestFileSystem) { + throw new MyFileError({ + code: FileError.BROWSER_NOT_SUPPORTED, + name: 'BROWSER_NOT_SUPPORTED' + }); + } + + var initObj = opt_initObj ? opt_initObj : {}; // Use defaults if obj is null. + + var size = initObj.size || DEFAULT_FS_SIZE; + this.type = self.TEMPORARY; + if ('persistent' in initObj && initObj.persistent) { + this.type = self.PERSISTENT; + } + + var init = function(fs) { + this.size = size; + fs_ = fs; + cwd_ = fs_.root; + isOpen_ = true; + + opt_successCallback && opt_successCallback(fs); + }; + + if (this.type == self.PERSISTENT && !!navigator.persistentStorage) { + navigator.persistentStorage.requestQuota(size, function(grantedBytes) { + self.requestFileSystem( + this.type, grantedBytes, init.bind(this), opt_errorHandler); + }.bind(this), opt_errorHandler); + } else { + self.requestFileSystem( + this.type, size, init.bind(this), opt_errorHandler); + } + }; + + /** + * Reads the contents of a directory. + * + * @param {string|DirectoryEntry} dirEntryOrPath A path relative to the + * current working directory. In most cases that is the root entry, unless + * cd() has been called. A DirectoryEntry or filesystem URL can also be + * passed, in which case, the folder's contents will be returned. + * @param {Function} successCallback Success handler passed an Array. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.ls = function(dirEntryOrPath, successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var callback = function(dirEntry) { + + cwd_ = dirEntry; + + // Read contents of current working directory. According to spec, need to + // keep calling readEntries() until length of result array is 0. We're + // guarenteed the same entry won't be returned again. + var entries_ = []; + var reader = cwd_.createReader(); + + var readEntries = function() { + reader.readEntries(function(results) { + if (!results.length) { + // By default, sort the list by name. + entries_.sort(function(a, b) { + return a.name < b.name ? -1 : b.name < a.name ? 1 : 0; + }); + successCallback(entries_); + } else { + entries_ = entries_.concat(Util.toArray(results)); + readEntries(); + } + }, opt_errorHandler); + }; + + readEntries(); + }; + + if (dirEntryOrPath.isDirectory) { // passed a DirectoryEntry. + callback(dirEntryOrPath); + } else if (isFsURL_(dirEntryOrPath)) { // passed a filesystem URL. + getEntry_(callback, dirEntryOrPath); + } else { // Passed a path. Look up DirectoryEntry and proceeed. + // TODO: Find way to use getEntry_(callback, dirEntryOrPath); with cwd_. + cwd_.getDirectory(dirEntryOrPath, {}, callback, opt_errorHandler); + } + }; + + /** + * Creates a new directory. + * + * @param {string} path The name of the directory to create. If a path is + * given, each intermediate dir is created (e.g. similar to mkdir -p). + * @param {bool=} opt_exclusive True if an error should be thrown if + * one or more of the directories already exists. False by default. + * @param {Function} opt_successCallback Success handler passed the + * DirectoryEntry that was created. If we were passed a path, the last + * directory that was created is passed back. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.mkdir = function(path, opt_exclusive, opt_successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var exclusive = opt_exclusive != null ? opt_exclusive : false; + + var folderParts = path.split('/'); + + var createDir = function(rootDir, folders) { + // Throw out './' or '/' and move on. Prevents: '/foo/.//bar'. + if (folders[0] == '.' || folders[0] == '') { + folders = folders.slice(1); + } + + rootDir.getDirectory(folders[0], {create: true, exclusive: exclusive}, + function (dirEntry) { + if (dirEntry.isDirectory) { // TODO: check shouldn't be necessary. + // Recursively add the new subfolder if we have more to create and + // There was more than one folder to create. + if (folders.length && folderParts.length != 1) { + createDir(dirEntry, folders.slice(1)); + } else { + // Return the last directory that was created. + if (opt_successCallback) opt_successCallback(dirEntry); + } + } else { + var e = new Error(path + ' is not a directory'); + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + }, + function(e) { + if (e.code == FileError.INVALID_MODIFICATION_ERR) { + e.message = "'" + path + "' already exists"; + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + } + ); + }; + + createDir(cwd_, folderParts); + }; + + /** + * Looks up and return a File for a given file entry. + * + * @param {string|FileEntry} entryOrPath A path, filesystem URL, or FileEntry + * of the file to lookup. + * @param {Function} successCallback Success callback passed the File object. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.open = function(entryOrPath, successCallback, opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + if (entryOrPath.isFile) { + entryOrPath.file(successCallback, opt_errorHandler); + } else { + getEntry_(function(fileEntry) { + fileEntry.file(successCallback, opt_errorHandler); + }, pathToFsURL_(entryOrPath)); + } + }; + + /** + * Creates an empty file. + * + * @param {string} path The relative path of the file to create, from the + * current working directory. + * @param {bool=} opt_exclusive True (default) if an error should be thrown if + * the file already exists. + * @param {Function} successCallback A success callback, which is passed + * the new FileEntry. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.create = function(path, opt_exclusive, successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var exclusive = opt_exclusive != null ? opt_exclusive : true; + + cwd_.getFile(path, {create: true, exclusive: exclusive}, successCallback, + function(e) { + if (e.code == FileError.INVALID_MODIFICATION_ERR) { + e.message = "'" + path + "' already exists"; + } + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + ); + }; + + /** + * Moves a file or directory. + * + * @param {string|FileEntry|DirectoryEntry} src The file/directory + * to move. If src is a string, a path or filesystem: URL is accepted. + * @param {string|DirectoryEntry} dest The directory to move the src into. + * If dest is a string, a path or filesystem: URL is accepted. + * Note: dest needs to be the same type as src. + * @param {string=} opt_newName An optional new name for the moved entry. + * @param {Function=} opt_successCallback Optional callback passed the moved + * entry on a successful move. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.mv = function(src, dest, opt_newName, opt_successCallback, + opt_errorHandler) { + copyOrMove_.bind(this, src, dest, opt_newName, opt_successCallback, + opt_errorHandler, true)(); + }; + + /** + * Deletes a file or directory entry. + * + * @param {string|FileEntry|DirectoryEntry} entryOrPath The file or directory + * to remove. If entry is a DirectoryEntry, its contents are removed + * recursively. If entryOrPath is a string, a path or filesystem: URL is + * accepted. + * @param {Function} successCallback Zero arg callback invoked on + * successful removal. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.rm = function(entryOrPath, successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var removeIt = function(entry) { + if (entry.isFile) { + entry.remove(successCallback, opt_errorHandler); + } else if (entry.isDirectory) { + entry.removeRecursively(successCallback, opt_errorHandler); + } + }; + + if (entryOrPath.isFile || entryOrPath.isDirectory) { + removeIt(entryOrPath); + } else { + getEntry_(removeIt, entryOrPath); + } + }; + + /** + * Changes the current working directory. + * + * @param {string|DirectoryEntry} dirEntryOrPath A DirectoryEntry to move into + * or a path relative to the current working directory. A filesystem: URL + * is also accepted + * @param {Function=} opt_successCallback Optional success callback, which is + * passed the DirectoryEntry of the new current directory. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.cd = function(dirEntryOrPath, opt_successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + if (dirEntryOrPath.isDirectory) { + cwd_ = dirEntryOrPath; + opt_successCallback && opt_successCallback(cwd_); + } else { + // Build a filesystem: URL manually if we need to. + var dirEntryOrPath = pathToFsURL_(dirEntryOrPath); + + getEntry_(function(dirEntry) { + if (dirEntry.isDirectory) { + cwd_ = dirEntry; + opt_successCallback && opt_successCallback(cwd_); + } else { + var e = new Error(NOT_A_DIRECTORY); + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + }, dirEntryOrPath); + } + }; + + /** + * Copies a file or directory to a destination. + * + * @param {string|FileEntry|DirectoryEntry} src The file/directory + * to copy. If src is a string, a path or filesystem: URL is accepted. + * @param {string|DirectoryEntry} dest The directory to copy the src into. + * If dest is a string, a path or filesystem: URL is accepted. + * Note: dest needs to be the same type as src. + * @param {string=} opt_newName An optional name for the copied entry. + * @param {Function=} opt_successCallback Optional callback passed the moved + * entry on a successful copy. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.cp = function(src, dest, opt_newName, opt_successCallback, + opt_errorHandler) { + copyOrMove_.bind(this, src, dest, opt_newName, opt_successCallback, + opt_errorHandler)(); + }; + + /** + * Writes data to a file. + * + * If the file already exists, its contents are overwritten. + * + * @param {string|FileEntry} entryOrPath A path, filesystem URL, or FileEntry + * of the file to lookup. + * @param {object} dataObj The data to write. Example: + * {data: string|Blob|File|ArrayBuffer, type: mimetype, append: true} + * If append is specified, data is appended to the end of the file. + * @param {Function} opt_successCallback Success callback, which is passed + * the created FileEntry and FileWriter object used to write the data. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.write = function(entryOrPath, dataObj, opt_successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var writeFile_ = function(fileEntry) { + fileEntry.createWriter(function(fileWriter) { + + fileWriter.onerror = opt_errorHandler; + + if (dataObj.append) { + fileWriter.onwriteend = function(e) { + if (opt_successCallback) opt_successCallback(fileEntry, this); + }; + + fileWriter.seek(fileWriter.length); // Start write position at EOF. + } else { + var truncated = false; + fileWriter.onwriteend = function(e) { + // Truncate file to newly written file size. + if (!truncated) { + truncated = true; + this.truncate(this.position); + return; + } + if (opt_successCallback) opt_successCallback(fileEntry, this); + }; + } + + // Blob() takes ArrayBufferView, not ArrayBuffer. + if (dataObj.data.__proto__ == ArrayBuffer.prototype) { + dataObj.data = new Uint8Array(dataObj.data); + } + var blob = new Blob([dataObj.data], + dataObj.type ? {type: dataObj.type} : {}); + + fileWriter.write(blob); + + }, opt_errorHandler); + }; + + if (entryOrPath.isFile) { + writeFile_(entryOrPath); + } else if (isFsURL_(entryOrPath)) { + getEntry_(writeFile_, entryOrPath); + } else { + cwd_.getFile(entryOrPath, {create: true, exclusive: false}, writeFile_, + opt_errorHandler); + } + }; + + /** + * Displays disk space usage. + * + * @param {Function} successCallback Success callback, which is passed + * Used space, Free space and Currently allocated total space in bytes. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.df = function(successCallback, opt_errorHandler) { + var queryCallback = function(byteUsed, byteCap) { + successCallback(byteUsed, byteCap - byteUsed, byteCap); + } + + if (!(navigator.temporaryStorage.queryUsageAndQuota && navigator.persistentStorage.queryUsageAndQuota)) { + throw new Error(NOT_IMPLEMENTED_MSG); + } + + if (self.TEMPORARY == this.type) { + navigator.temporaryStorage.queryUsageAndQuota(queryCallback, opt_errorHandler); + } else if (self.PERSISTENT == this.type) { + navigator.persistentStorage.queryUsageAndQuota(queryCallback, opt_errorHandler); + } + }; + + return Filer; +}; \ No newline at end of file diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/0__Code_Dev/Javascript_approach/Packages_libraries_tools/filerjs/src/filer.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/1__Development/0__Code_Dev/Javascript_approach/Packages_libraries_tools/filerjs/src/filer.js Sat Aug 09 03:34:26 2014 -0700 @@ -0,0 +1,836 @@ +/** + * Copyright 2013 - Eric Bidelman + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + * @fileoverview + * Convenient wrapper library for the HTML5 Filesystem API, implementing + * familiar UNIX commands (cp, mv, ls) for its API. + * + * @author Eric Bidelman (ebidel@gmail.com) + * @version: 0.4.3 + */ + +'use strict'; + +var self = this; // window or worker context. + +self.URL = self.URL || self.webkitURL; +self.requestFileSystem = self.requestFileSystem || self.webkitRequestFileSystem; +self.resolveLocalFileSystemURL = self.resolveLocalFileSystemURL || + self.webkitResolveLocalFileSystemURL; +navigator.temporaryStorage = navigator.temporaryStorage || + navigator.webkitTemporaryStorage; +navigator.persistentStorage = navigator.persistentStorage || + navigator.webkitPersistentStorage; +self.BlobBuilder = self.BlobBuilder || self.MozBlobBuilder || + self.WebKitBlobBuilder; + +// Prevent errors in browsers that don't support FileError. +if (self.FileError === undefined) { + var FileError = function() {}; + FileError.prototype.prototype = Error.prototype; +} + +var Util = { + + /** + * Turns a NodeList into an array. + * + * @param {NodeList} list The array-like object. + * @return {Array} The NodeList as an array. + */ + toArray: function(list) { + return Array.prototype.slice.call(list || [], 0); + }, + + /*toDataURL: function(contentType, uint8Array) { + return 'data:' + contentType + ';base64,' + + self.btoa(this.arrayToBinaryString(uint8Array)); + },*/ + + /** + * Creates a data: URL from string data. + * + * @param {string} str The content to encode the data: URL from. + * @param {string} contentType The mimetype of the data str represents. + * @param {bool=} opt_isBinary Whether the string data is a binary string + * (and therefore should be base64 encoded). True by default. + * @return {string} The created data: URL. + */ + strToDataURL: function(str, contentType, opt_isBinary) { + var isBinary = opt_isBinary != undefined ? opt_isBinary : true; + if (isBinary) { + return 'data:' + contentType + ';base64,' + self.btoa(str); + } else { + return 'data:' + contentType + ',' + str; + } + }, + + /** + * Creates a blob: URL from a binary str. + * + * @param {string} binStr The content as a binary string. + * @param {string=} opt_contentType An optional mimetype of the data. + * @return {string} A new blob: URL. + */ + strToObjectURL: function(binStr, opt_contentType) { + + var ui8a = new Uint8Array(binStr.length); + for (var i = 0; i < ui8a.length; ++i) { + ui8a[i] = binStr.charCodeAt(i); + } + + var blob = new Blob([ui8a], + opt_contentType ? {type: opt_contentType} : {}); + + return self.URL.createObjectURL(blob); + }, + + /** + * Creates a blob: URL from a File or Blob object. + * + * @param {Blob|File} blob The File or Blob data. + * @return {string} A new blob: URL. + */ + fileToObjectURL: function(blob) { + return self.URL.createObjectURL(blob); + }, + + /** + * Reads a File or Blob object and returns it as an ArrayBuffer. + * + * @param {Blob|File} blob The File or Blob data. + * @param {Function} callback Success callback passed the array buffer. + * @param {Function=} opt_error Optional error callback if the read fails. + */ + fileToArrayBuffer: function(blob, callback, opt_errorCallback) { + var reader = new FileReader(); + reader.onload = function(e) { + callback(e.target.result); + }; + reader.onerror = function(e) { + if (opt_errorCallback) { + opt_errorCallback(e); + } + }; + + reader.readAsArrayBuffer(blob); + }, + + /** + * Creates and returns a blob from a data URL (either base64 encoded or not). + * + * @param {string} dataURL The data URL to convert. + * @return {Blob} A blob representing the array buffer data. + */ + dataURLToBlob: function(dataURL) { + var BASE64_MARKER = ';base64,'; + if (dataURL.indexOf(BASE64_MARKER) == -1) { + var parts = dataURL.split(','); + var contentType = parts[0].split(':')[1]; + var raw = decodeURIComponent(parts[1]); + + return new Blob([raw], {type: contentType}); + } + + var parts = dataURL.split(BASE64_MARKER); + var contentType = parts[0].split(':')[1]; + var raw = window.atob(parts[1]); + var rawLength = raw.length; + + var uInt8Array = new Uint8Array(rawLength); + + for (var i = 0; i < rawLength; ++i) { + uInt8Array[i] = raw.charCodeAt(i); + } + + return new Blob([uInt8Array], {type: contentType}); + }, + + /** + * Reads an ArrayBuffer as returns its contents as a binary string. + * + * @param {ArrayBuffer} buffer The buffer of data. + * @param {string=} opt_contentType An optional mimetype of the data. + * @return {Blob} A blob representing the array buffer data. + */ + arrayBufferToBlob: function(buffer, opt_contentType) { + var uInt8Array = new Uint8Array(buffer); + return new Blob([uInt8Array], + opt_contentType ? {type: opt_contentType} : {}); + }, + + /** + * Reads an ArrayBuffer as returns its contents as a binary string. + * + * @param {ArrayBuffer} buffer The buffer of data. + * @param {Function} callback Success callback passed the binary string. + * @param {Function=} opt_error Optional error callback if the read fails. + */ + arrayBufferToBinaryString: function(buffer, callback, opt_errorCallback) { + var reader = new FileReader(); + reader.onload = function(e) { + callback(e.target.result); + }; + reader.onerror = function(e) { + if (opt_errorCallback) { + opt_errorCallback(e); + } + }; + + var uInt8Array = new Uint8Array(buffer); + reader.readAsBinaryString(new Blob([uInt8Array])); + }, + + /** + * Create a binary string out of an array of numbers (bytes), each varying + * from 0-255. + * + * @param {Array} bytes The array of numbers to transform into a binary str. + * @return {string} The byte array as a string. + */ + arrayToBinaryString: function(bytes) { + if (typeof bytes != typeof []) { + return null; + } + var i = bytes.length; + var bstr = new Array(i); + while (i--) { + bstr[i] = String.fromCharCode(bytes[i]); + } + return bstr.join(''); + }, + + /** + * Returns the file extension for a given filename. + * + * @param {string} filename The filename. + * @return {string} The file's extension. + */ + getFileExtension: function(filename) { + var idx = filename.lastIndexOf('.'); + return idx != -1 ? filename.substring(idx) : ''; + } +}; + + +var MyFileError = function(obj) { + this.prototype = FileError.prototype; + this.code = obj.code; + this.name = obj.name; +}; +//MyFileError.prototype.__proto__ = FileError.prototype; + +// Extend FileError with custom errors and a convenience method to get error +// code mnemonic. +FileError.BROWSER_NOT_SUPPORTED = 1000; + +// TODO: remove when FileError.name is implemented (crbug.com/86014). +FileError.prototype.__defineGetter__('name', function() { + var keys = Object.keys(FileError); + for (var i = 0, key; key = keys[i]; ++i) { + if (FileError[key] == this.code) { + return key; + } + } + return 'Unknown Error'; +}); + + +var Filer = new function() { + + var FS_INIT_ERROR_MSG = 'Filesystem has not been initialized.'; + var NOT_IMPLEMENTED_MSG = 'Not implemented.'; + var NOT_A_DIRECTORY = 'Path was not a directory.'; + var INCORRECT_ARGS = 'These method arguments are not supported.'; + var FS_URL_SCHEME = 'filesystem:'; + var DEFAULT_FS_SIZE = 1024 * 1024; // 1MB. + + var fs_ = null; + var cwd_ = null; + var isOpen_ = false; + + var isFsURL_ = function(path) { + return path.indexOf(FS_URL_SCHEME) == 0; + }; + + // Path can be relative or absolute. If relative, it's taken from the cwd_. + // If a filesystem URL is passed it, it is simple returned + var pathToFsURL_ = function(path) { + if (!isFsURL_(path)) { + if (path[0] == '/') { + path = fs_.root.toURL() + path.substring(1); + } else if (path.indexOf('./') == 0 || path.indexOf('../') == 0) { + if (path == '../' && cwd_ != fs_.root) { + path = cwd_.toURL() + '/' + path; + } else { + path = cwd_.toURL() + path; + } + } else { + path = cwd_.toURL() + '/' + path; + } + } + + return path; + }; + + /** + * Looks up a FileEntry or DirectoryEntry for a given path. + * + * @param {function(...FileEntry|DirectorEntry)} callback A callback to be + * passed the entry/entries that were fetched. The ordering of the + * entries passed to the callback correspond to the same order passed + * to this method. + * @param {...string} var_args 1-2 paths to lookup and return entries for. + * These can be paths or filesystem: URLs. + */ + var getEntry_ = function(callback, var_args) { + var srcStr = arguments[1]; + var destStr = arguments[2]; + + var onError = function(e) { + if (e.code == FileError.NOT_FOUND_ERR) { + if (destStr) { + throw new Error('"' + srcStr + '" or "' + destStr + + '" does not exist.'); + } else { + throw new Error('"' + srcStr + '" does not exist.'); + } + } else { + throw new Error('Problem getting Entry for one or more paths.'); + } + }; + + // Build a filesystem: URL manually if we need to. + var src = pathToFsURL_(srcStr); + + if (arguments.length == 3) { + var dest = pathToFsURL_(destStr); + self.resolveLocalFileSystemURL(src, function(srcEntry) { + self.resolveLocalFileSystemURL(dest, function(destEntry) { + callback(srcEntry, destEntry); + }, onError); + }, onError); + } else { + self.resolveLocalFileSystemURL(src, callback, onError); + } + }; + + /** + * Copy or moves a file or directory to a destination. + * + * See public method's description (Filer.cp()) for rest of params. + * @param {Boolean=} opt_deleteOrig True if the original entry should be + * deleted after the copy takes place, essentially making the operation + * a move instead of a copy. Defaults to false. + */ + var copyOrMove_ = function(src, dest, opt_newName, opt_successCallback, + opt_errorHandler, opt_deleteOrig) { + var self = this; + + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + if (typeof src != typeof dest) { + throw new Error(INCORRECT_ARGS); + } + + var newName = opt_newName || null; + var deleteOrig = opt_deleteOrig != undefined ? opt_deleteOrig : false; + + if ((src.isFile || dest.isDirectory) && dest.isDirectory) { + if (deleteOrig) { + src.moveTo(dest, newName, opt_successCallback, opt_errorHandler); + } else { + src.copyTo(dest, newName, opt_successCallback, opt_errorHandler); + } + } else { + getEntry_(function(srcEntry, destDir) { + if (!destDir.isDirectory) { + var e = new Error('Oops! "' + destDir.name + ' is not a directory!'); + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + return; + } + if (deleteOrig) { + srcEntry.moveTo(destDir, newName, opt_successCallback, opt_errorHandler); + } else { + srcEntry.copyTo(destDir, newName, opt_successCallback, opt_errorHandler); + } + }, src, dest); + } + } + + function Filer(fs) { + fs_ = fs || null; + if (fs_) { + cwd_ = fs_.root; + isOpen_ = true; // TODO: this may not be the case. + } + } + + Filer.DEFAULT_FS_SIZE = DEFAULT_FS_SIZE; + Filer.version = '0.4.3'; + + Filer.prototype = { + get fs() { + return fs_; + }, + get isOpen() { + return isOpen_; + }, + get cwd() { + return cwd_; + } + } + + /** + * Constructs and returns a filesystem: URL given a path. + * + * @param {string=} path The path to construct a URL for. + * size {int=} The storage size (in bytes) to open the filesystem with. + * Defaults to DEFAULT_FS_SIZE. + * @return {string} The filesystem: URL. + */ + Filer.prototype.pathToFilesystemURL = function(path) { + return pathToFsURL_(path); + } + + /** + * Initializes (opens) the file system. + * + * @param {object=} opt_initObj Optional object literal with the following + * properties. Note: If {} or null is passed, default values are used. + * persistent {Boolean=} Whether the browser should use persistent quota. + * Default is false. + * size {int=} The storage size (in bytes) to open the filesystem with. + * Defaults to DEFAULT_FS_SIZE. + * @param {Function=} opt_successCallback Optional success handler passed a + * DOMFileSystem object. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.init = function(opt_initObj, opt_successCallback, + opt_errorHandler) { + if (!self.requestFileSystem) { + throw new MyFileError({ + code: FileError.BROWSER_NOT_SUPPORTED, + name: 'BROWSER_NOT_SUPPORTED' + }); + } + + var initObj = opt_initObj ? opt_initObj : {}; // Use defaults if obj is null. + + var size = initObj.size || DEFAULT_FS_SIZE; + this.type = self.TEMPORARY; + if ('persistent' in initObj && initObj.persistent) { + this.type = self.PERSISTENT; + } + + var init = function(fs) { + this.size = size; + fs_ = fs; + cwd_ = fs_.root; + isOpen_ = true; + + opt_successCallback && opt_successCallback(fs); + }; + + if (this.type == self.PERSISTENT && !!navigator.persistentStorage) { + navigator.persistentStorage.requestQuota(size, function(grantedBytes) { + self.requestFileSystem( + this.type, grantedBytes, init.bind(this), opt_errorHandler); + }.bind(this), opt_errorHandler); + } else { + self.requestFileSystem( + this.type, size, init.bind(this), opt_errorHandler); + } + }; + + /** + * Reads the contents of a directory. + * + * @param {string|DirectoryEntry} dirEntryOrPath A path relative to the + * current working directory. In most cases that is the root entry, unless + * cd() has been called. A DirectoryEntry or filesystem URL can also be + * passed, in which case, the folder's contents will be returned. + * @param {Function} successCallback Success handler passed an Array. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.ls = function(dirEntryOrPath, successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var callback = function(dirEntry) { + + cwd_ = dirEntry; + + // Read contents of current working directory. According to spec, need to + // keep calling readEntries() until length of result array is 0. We're + // guarenteed the same entry won't be returned again. + var entries_ = []; + var reader = cwd_.createReader(); + + var readEntries = function() { + reader.readEntries(function(results) { + if (!results.length) { + // By default, sort the list by name. + entries_.sort(function(a, b) { + return a.name < b.name ? -1 : b.name < a.name ? 1 : 0; + }); + successCallback(entries_); + } else { + entries_ = entries_.concat(Util.toArray(results)); + readEntries(); + } + }, opt_errorHandler); + }; + + readEntries(); + }; + + if (dirEntryOrPath.isDirectory) { // passed a DirectoryEntry. + callback(dirEntryOrPath); + } else if (isFsURL_(dirEntryOrPath)) { // passed a filesystem URL. + getEntry_(callback, dirEntryOrPath); + } else { // Passed a path. Look up DirectoryEntry and proceeed. + // TODO: Find way to use getEntry_(callback, dirEntryOrPath); with cwd_. + cwd_.getDirectory(dirEntryOrPath, {}, callback, opt_errorHandler); + } + }; + + /** + * Creates a new directory. + * + * @param {string} path The name of the directory to create. If a path is + * given, each intermediate dir is created (e.g. similar to mkdir -p). + * @param {bool=} opt_exclusive True if an error should be thrown if + * one or more of the directories already exists. False by default. + * @param {Function} opt_successCallback Success handler passed the + * DirectoryEntry that was created. If we were passed a path, the last + * directory that was created is passed back. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.mkdir = function(path, opt_exclusive, opt_successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var exclusive = opt_exclusive != null ? opt_exclusive : false; + + var folderParts = path.split('/'); + + var createDir = function(rootDir, folders) { + // Throw out './' or '/' and move on. Prevents: '/foo/.//bar'. + if (folders[0] == '.' || folders[0] == '') { + folders = folders.slice(1); + } + + rootDir.getDirectory(folders[0], {create: true, exclusive: exclusive}, + function (dirEntry) { + if (dirEntry.isDirectory) { // TODO: check shouldn't be necessary. + // Recursively add the new subfolder if we have more to create and + // There was more than one folder to create. + if (folders.length && folderParts.length != 1) { + createDir(dirEntry, folders.slice(1)); + } else { + // Return the last directory that was created. + if (opt_successCallback) opt_successCallback(dirEntry); + } + } else { + var e = new Error(path + ' is not a directory'); + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + }, + function(e) { + if (e.code == FileError.INVALID_MODIFICATION_ERR) { + e.message = "'" + path + "' already exists"; + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + } + ); + }; + + createDir(cwd_, folderParts); + }; + + /** + * Looks up and return a File for a given file entry. + * + * @param {string|FileEntry} entryOrPath A path, filesystem URL, or FileEntry + * of the file to lookup. + * @param {Function} successCallback Success callback passed the File object. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.open = function(entryOrPath, successCallback, opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + if (entryOrPath.isFile) { + entryOrPath.file(successCallback, opt_errorHandler); + } else { + getEntry_(function(fileEntry) { + fileEntry.file(successCallback, opt_errorHandler); + }, pathToFsURL_(entryOrPath)); + } + }; + + /** + * Creates an empty file. + * + * @param {string} path The relative path of the file to create, from the + * current working directory. + * @param {bool=} opt_exclusive True (default) if an error should be thrown if + * the file already exists. + * @param {Function} successCallback A success callback, which is passed + * the new FileEntry. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.create = function(path, opt_exclusive, successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var exclusive = opt_exclusive != null ? opt_exclusive : true; + + cwd_.getFile(path, {create: true, exclusive: exclusive}, successCallback, + function(e) { + if (e.code == FileError.INVALID_MODIFICATION_ERR) { + e.message = "'" + path + "' already exists"; + } + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + ); + }; + + /** + * Moves a file or directory. + * + * @param {string|FileEntry|DirectoryEntry} src The file/directory + * to move. If src is a string, a path or filesystem: URL is accepted. + * @param {string|DirectoryEntry} dest The directory to move the src into. + * If dest is a string, a path or filesystem: URL is accepted. + * Note: dest needs to be the same type as src. + * @param {string=} opt_newName An optional new name for the moved entry. + * @param {Function=} opt_successCallback Optional callback passed the moved + * entry on a successful move. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.mv = function(src, dest, opt_newName, opt_successCallback, + opt_errorHandler) { + copyOrMove_.bind(this, src, dest, opt_newName, opt_successCallback, + opt_errorHandler, true)(); + }; + + /** + * Deletes a file or directory entry. + * + * @param {string|FileEntry|DirectoryEntry} entryOrPath The file or directory + * to remove. If entry is a DirectoryEntry, its contents are removed + * recursively. If entryOrPath is a string, a path or filesystem: URL is + * accepted. + * @param {Function} successCallback Zero arg callback invoked on + * successful removal. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.rm = function(entryOrPath, successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var removeIt = function(entry) { + if (entry.isFile) { + entry.remove(successCallback, opt_errorHandler); + } else if (entry.isDirectory) { + entry.removeRecursively(successCallback, opt_errorHandler); + } + }; + + if (entryOrPath.isFile || entryOrPath.isDirectory) { + removeIt(entryOrPath); + } else { + getEntry_(removeIt, entryOrPath); + } + }; + + /** + * Changes the current working directory. + * + * @param {string|DirectoryEntry} dirEntryOrPath A DirectoryEntry to move into + * or a path relative to the current working directory. A filesystem: URL + * is also accepted + * @param {Function=} opt_successCallback Optional success callback, which is + * passed the DirectoryEntry of the new current directory. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.cd = function(dirEntryOrPath, opt_successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + if (dirEntryOrPath.isDirectory) { + cwd_ = dirEntryOrPath; + opt_successCallback && opt_successCallback(cwd_); + } else { + // Build a filesystem: URL manually if we need to. + var dirEntryOrPath = pathToFsURL_(dirEntryOrPath); + + getEntry_(function(dirEntry) { + if (dirEntry.isDirectory) { + cwd_ = dirEntry; + opt_successCallback && opt_successCallback(cwd_); + } else { + var e = new Error(NOT_A_DIRECTORY); + if (opt_errorHandler) { + opt_errorHandler(e); + } else { + throw e; + } + } + }, dirEntryOrPath); + } + }; + + /** + * Copies a file or directory to a destination. + * + * @param {string|FileEntry|DirectoryEntry} src The file/directory + * to copy. If src is a string, a path or filesystem: URL is accepted. + * @param {string|DirectoryEntry} dest The directory to copy the src into. + * If dest is a string, a path or filesystem: URL is accepted. + * Note: dest needs to be the same type as src. + * @param {string=} opt_newName An optional name for the copied entry. + * @param {Function=} opt_successCallback Optional callback passed the moved + * entry on a successful copy. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.cp = function(src, dest, opt_newName, opt_successCallback, + opt_errorHandler) { + copyOrMove_.bind(this, src, dest, opt_newName, opt_successCallback, + opt_errorHandler)(); + }; + + /** + * Writes data to a file. + * + * If the file already exists, its contents are overwritten. + * + * @param {string|FileEntry} entryOrPath A path, filesystem URL, or FileEntry + * of the file to lookup. + * @param {object} dataObj The data to write. Example: + * {data: string|Blob|File|ArrayBuffer, type: mimetype, append: true} + * If append is specified, data is appended to the end of the file. + * @param {Function} opt_successCallback Success callback, which is passed + * the created FileEntry and FileWriter object used to write the data. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.write = function(entryOrPath, dataObj, opt_successCallback, + opt_errorHandler) { + if (!fs_) { + throw new Error(FS_INIT_ERROR_MSG); + } + + var writeFile_ = function(fileEntry) { + fileEntry.createWriter(function(fileWriter) { + + fileWriter.onerror = opt_errorHandler; + + if (dataObj.append) { + fileWriter.onwriteend = function(e) { + if (opt_successCallback) opt_successCallback(fileEntry, this); + }; + + fileWriter.seek(fileWriter.length); // Start write position at EOF. + } else { + var truncated = false; + fileWriter.onwriteend = function(e) { + // Truncate file to newly written file size. + if (!truncated) { + truncated = true; + this.truncate(this.position); + return; + } + if (opt_successCallback) opt_successCallback(fileEntry, this); + }; + } + + // Blob() takes ArrayBufferView, not ArrayBuffer. + if (dataObj.data.__proto__ == ArrayBuffer.prototype) { + dataObj.data = new Uint8Array(dataObj.data); + } + var blob = new Blob([dataObj.data], + dataObj.type ? {type: dataObj.type} : {}); + + fileWriter.write(blob); + + }, opt_errorHandler); + }; + + if (entryOrPath.isFile) { + writeFile_(entryOrPath); + } else if (isFsURL_(entryOrPath)) { + getEntry_(writeFile_, entryOrPath); + } else { + cwd_.getFile(entryOrPath, {create: true, exclusive: false}, writeFile_, + opt_errorHandler); + } + }; + + /** + * Displays disk space usage. + * + * @param {Function} successCallback Success callback, which is passed + * Used space, Free space and Currently allocated total space in bytes. + * @param {Function=} opt_errorHandler Optional error callback. + */ + Filer.prototype.df = function(successCallback, opt_errorHandler) { + var queryCallback = function(byteUsed, byteCap) { + successCallback(byteUsed, byteCap - byteUsed, byteCap); + } + + if (!(navigator.temporaryStorage.queryUsageAndQuota && navigator.persistentStorage.queryUsageAndQuota)) { + throw new Error(NOT_IMPLEMENTED_MSG); + } + + if (self.TEMPORARY == this.type) { + navigator.temporaryStorage.queryUsageAndQuota(queryCallback, opt_errorHandler); + } else if (self.PERSISTENT == this.type) { + navigator.persistentStorage.queryUsageAndQuota(queryCallback, opt_errorHandler); + } + }; + + return Filer; +}; diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/6__Website/POPBottleServer/POPBottleServer.py --- a/1__Development/6__Website/POPBottleServer/POPBottleServer.py Thu Aug 07 00:10:50 2014 -0700 +++ b/1__Development/6__Website/POPBottleServer/POPBottleServer.py Sat Aug 09 03:34:26 2014 -0700 @@ -1,32 +1,47 @@ from bottle import route, run, template, response import urllib -graphFile = None - #============== - -@route('/startsavinggraph') -def startsavinggraph(): - global graphFile +# Persisting the graph to disk +#============== +@route('/cleargraph') +def cleargraph(): graphFile = open("syntaxGraph.json", "w") - print "opened graph file " - print "Name of the file: ", graphFile.name - print "Closed or not : ", graphFile.closed - print "Opening mode : ", graphFile.mode - print "Softspace flag : ", graphFile.softspace + print "opened graph file, which should have cleared it" + graphFile.close() response.set_header('Access-Control-Allow-Origin', '*') @route('/save1elem/') def save1elem(thejson): - thejson = thejson.replace('%T', '\t') - thejson = thejson.replace('%N', '\n') - thejson = thejson.replace('%H', '\\') - thejson = thejson.replace('%S', '/') -# print thejson - global graphFile - graphFile.write("%s\nend elem\n" % thejson) + thejson = thejson.replace('%T', '\t') + thejson = thejson.replace('%N', '\n') + thejson = thejson.replace('%H', '\\') + thejson = thejson.replace('%S', '/') +# print thejson + graphFile = open("syntaxGraph.json", "a") + graphFile.write("%s\nseparator\n" % thejson) + graphFile.close() + response.set_header('Access-Control-Allow-Origin', '*') + return "ACK" + +#================ +# Retrieving graph +#================ + +@route('/retrievegraph') +def retrieveGraph(): + graphFile = open("syntaxGraph.json", "r") + returnjson = graphFile.read() response.set_header('Access-Control-Allow-Origin', '*') + graphFile.close() + return returnjson +#================ + +#Deprecated -- at first, tried open via "start" URL, then write, then close +# here.. but issues in async mode. So now, just "start" clears the file +# then write opens in append mode, writes, and closes right away. +#So no longer need the "end" URL @route('/endsavinggraph') def endsavinggraph(): global graphFile @@ -34,8 +49,7 @@ graphFile.close() return 'ACK' -#================ - +#Deprecated -- start and end retrieve are deprecated @route('/startretrievinggraph') def startretrievinggraph(): global graphFile @@ -43,13 +57,6 @@ response.set_header('Access-Control-Allow-Origin', '*') return 'ACK' -@route('/get1elem') -def get1elem(): - global graphFile - returnjson = graphFile.readline() - response.set_header('Access-Control-Allow-Origin', '*') - return returnjson - @route('/endretrievinggraph') def endretrievinggraph(): global graphFile diff -r 20a1407463a0 -r cc10d99e3f83 1__Development/6__Website/POPBottleServer/syntaxGraph.json --- a/1__Development/6__Website/POPBottleServer/syntaxGraph.json Thu Aug 07 00:10:50 2014 -0700 +++ b/1__Development/6__Website/POPBottleServer/syntaxGraph.json Sat Aug 09 03:34:26 2014 -0700 @@ -1,9 +1,11 @@ { "rootElem": 1, "rootViewSet": { + "type": "ViewSet", "ID": 2, "viewSetLinks": [ { + "type": "ViewSetLink", "ID": 3, "referenceViewSet": 2, "subordinateViewSet": 4, @@ -14,16 +16,19 @@ ] } } -end elem +separator { + "type": "GraphElem", "ID": 31, "properties": [ { + "type": "GraphProperty", "ID": 32, "propertyName": "TypeOfElement", "propertyValue": "Command", "subProperties": [ { + "type": "GraphProperty", "ID": 33, "propertyName": "CommandID", "propertyValue": "GabePattPop", @@ -32,6 +37,7 @@ ] }, { + "type": "GraphProperty", "ID": 34, "propertyName": "TypeOfSyntacticStructure", "propertyValue": "syntacticPatternRoot", @@ -40,15 +46,18 @@ ], "portsIn": [ { + "type": "GraphPort", "ID": 35, "element": 31, "properties": [ { + "type": "GraphProperty", "ID": 36, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 37, "propertyName": "TypeOfCommData", "propertyValue": "GabePattStack", @@ -64,15 +73,18 @@ ], "portsOut": [ { + "type": "GraphPort", "ID": 38, "element": 31, "properties": [ { + "type": "GraphProperty", "ID": 39, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 40, "propertyName": "TypeOfCommData", "propertyValue": "GabePattStack", @@ -84,15 +96,18 @@ "pairedPorts": [] }, { + "type": "GraphPort", "ID": 41, "element": 31, "properties": [ { + "type": "GraphProperty", "ID": 42, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 43, "propertyName": "TypeOfCommData", "propertyValue": "float", @@ -107,16 +122,19 @@ "linkedElems": [], "isAlreadyVisited": true } -end elem +separator { + "type": "GraphElem", "ID": 18, "properties": [ { + "type": "GraphProperty", "ID": 19, "propertyName": "TypeOfElement", "propertyValue": "Command", "subProperties": [ { + "type": "GraphProperty", "ID": 20, "propertyName": "CommandID", "propertyValue": "GabePattPush", @@ -125,6 +143,7 @@ ] }, { + "type": "GraphProperty", "ID": 21, "propertyName": "TypeOfSyntacticStructure", "propertyValue": "syntacticPatternRoot", @@ -133,15 +152,18 @@ ], "portsIn": [ { + "type": "GraphPort", "ID": 22, "element": 18, "properties": [ { + "type": "GraphProperty", "ID": 23, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 24, "propertyName": "TypeOfCommData", "propertyValue": "GabePattStack", @@ -153,15 +175,18 @@ "pairedPorts": [] }, { + "type": "GraphPort", "ID": 25, "element": 18, "properties": [ { + "type": "GraphProperty", "ID": 26, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 27, "propertyName": "TypeOfCommData", "propertyValue": "float", @@ -175,15 +200,18 @@ ], "portsOut": [ { + "type": "GraphPort", "ID": 28, "element": 18, "properties": [ { + "type": "GraphProperty", "ID": 29, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 30, "propertyName": "TypeOfCommData", "propertyValue": "GabePattStack", @@ -200,17 +228,20 @@ "linkedElems": [], "isAlreadyVisited": true } -end elem +separator { + "type": "GraphElem", "ID": 12, "properties": [ { + "type": "GraphProperty", "ID": 13, "propertyName": "TypeOfElement", "propertyValue": "GabeQueryPattern", "subProperties": [] }, { + "type": "GraphProperty", "ID": 14, "propertyName": "TypeOfSyntacticStructure", "propertyValue": "syntacticHierarchy", @@ -225,16 +256,19 @@ ], "isAlreadyVisited": true } -end elem +separator { + "type": "GraphElem", "ID": 44, "properties": [ { + "type": "GraphProperty", "ID": 45, "propertyName": "TypeOfElement", "propertyValue": "Command", "subProperties": [ { + "type": "GraphProperty", "ID": 46, "propertyName": "CommandID", "propertyValue": "GabePassThrough", @@ -243,6 +277,7 @@ ] }, { + "type": "GraphProperty", "ID": 47, "propertyName": "TypeOfSyntacticStructure", "propertyValue": "syntacticPatternRoot", @@ -251,15 +286,18 @@ ], "portsIn": [ { + "type": "GraphPort", "ID": 48, "element": 44, "properties": [ { + "type": "GraphProperty", "ID": 49, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 50, "propertyName": "TypeOfCommData", "propertyValue": "GabePattStack", @@ -271,15 +309,18 @@ "pairedPorts": [] }, { + "type": "GraphPort", "ID": 51, "element": 44, "properties": [ { + "type": "GraphProperty", "ID": 52, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 53, "propertyName": "TypeOfCommData", "propertyValue": "float", @@ -293,15 +334,18 @@ ], "portsOut": [ { + "type": "GraphPort", "ID": 54, "element": 44, "properties": [ { + "type": "GraphProperty", "ID": 55, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 56, "propertyName": "TypeOfCommData", "propertyValue": "GabePattStack", @@ -313,15 +357,18 @@ "pairedPorts": [] }, { + "type": "GraphPort", "ID": 57, "element": 44, "properties": [ { + "type": "GraphProperty", "ID": 58, "propertyName": "TypeOfPort", "propertyValue": "dataComm", "subProperties": [ { + "type": "GraphProperty", "ID": 59, "propertyName": "TypeOfCommData", "propertyValue": "float", @@ -336,17 +383,20 @@ "linkedElems": [], "isAlreadyVisited": true } -end elem +separator { + "type": "GraphElem", "ID": 15, "properties": [ { + "type": "GraphProperty", "ID": 16, "propertyName": "TypeOfElement", "propertyValue": "GabeReplacePattern", "subProperties": [] }, { + "type": "GraphProperty", "ID": 17, "propertyName": "TypeOfSyntacticStructure", "propertyValue": "syntacticHierarchy", @@ -360,17 +410,20 @@ ], "isAlreadyVisited": true } -end elem +separator { + "type": "GraphElem", "ID": 1, "properties": [ { + "type": "GraphProperty", "ID": 10, "propertyName": "TypeOfElement", "propertyValue": "GabeTransformRule", "subProperties": [] }, { + "type": "GraphProperty", "ID": 11, "propertyName": "TypeOfSyntacticStructure", "propertyValue": "syntacticHierarchy", @@ -384,9 +437,11 @@ 15 ], "viewSet": { + "type": "ViewSet", "ID": 4, "syntaxElem": 1, "rootViewBox": { + "type": "ViewBox", "ID": 5, "shape": "", "width": 1000, @@ -397,6 +452,7 @@ "parent": 4, "children": [ { + "type": "ViewBox", "ID": 6, "shape": " properties ", "width": 49, @@ -413,6 +469,7 @@ ] }, { + "type": "ViewBox", "ID": 7, "shape": " portsIn ", "width": 33, @@ -429,6 +486,7 @@ ] }, { + "type": "ViewBox", "ID": 8, "shape": " portsOut ", "width": 42, @@ -445,6 +503,7 @@ ] }, { + "type": "ViewBox", "ID": 9, "shape": " linkedElems ", "width": 55, @@ -474,4 +533,4 @@ }, "isAlreadyVisited": true } -end elem +separator