├── public ├── explorer │ ├── res │ │ ├── Thumbs.db │ │ ├── add.png │ │ ├── down.gif │ │ ├── key.png │ │ ├── bodyBg.png │ │ ├── bodyImg.png │ │ ├── cancel.gif │ │ ├── delete.png │ │ ├── expand.gif │ │ ├── semi-bg.png │ │ ├── spinner.gif │ │ ├── subNavL.png │ │ ├── subNavR.png │ │ ├── arrow_down.png │ │ ├── arrow_up.png │ │ ├── arrowdown.gif │ │ ├── blueTriBig.gif │ │ ├── headerBg.png │ │ ├── lightning.png │ │ ├── lock_add.png │ │ ├── navPointer.gif │ │ ├── navPointer.png │ │ ├── page_copy.png │ │ ├── page_paste.png │ │ ├── persevere.jpg │ │ ├── persevere.png │ │ ├── selected.png │ │ ├── table_add.png │ │ ├── blueTriSmall.png │ │ ├── paste_plain.png │ │ ├── subContentBg.png │ │ ├── persevere-205.jpg │ │ ├── persevere-273.jpg │ │ ├── persevere-273.png │ │ ├── persevere-glow.png │ │ ├── invert-persevere.png │ │ ├── chart_organisation.png │ │ ├── invert-persevere-273.png │ │ ├── iframeHistory.html │ │ └── common.css │ ├── js │ │ └── dojo-persevere │ │ │ ├── README.txt │ │ │ ├── resources │ │ │ └── login │ │ │ │ ├── login.html │ │ │ │ └── register.html │ │ │ ├── LoginLink.js │ │ │ ├── persevere.js │ │ │ └── Login.js │ └── explorer.js └── explorer.html ├── README ├── package.json └── lib ├── persvr.js └── json-query.js /public/explorer/res/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/Thumbs.db -------------------------------------------------------------------------------- /public/explorer/res/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/add.png -------------------------------------------------------------------------------- /public/explorer/res/down.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/down.gif -------------------------------------------------------------------------------- /public/explorer/res/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/key.png -------------------------------------------------------------------------------- /public/explorer/res/bodyBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/bodyBg.png -------------------------------------------------------------------------------- /public/explorer/res/bodyImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/bodyImg.png -------------------------------------------------------------------------------- /public/explorer/res/cancel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/cancel.gif -------------------------------------------------------------------------------- /public/explorer/res/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/delete.png -------------------------------------------------------------------------------- /public/explorer/res/expand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/expand.gif -------------------------------------------------------------------------------- /public/explorer/res/semi-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/semi-bg.png -------------------------------------------------------------------------------- /public/explorer/res/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/spinner.gif -------------------------------------------------------------------------------- /public/explorer/res/subNavL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/subNavL.png -------------------------------------------------------------------------------- /public/explorer/res/subNavR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/subNavR.png -------------------------------------------------------------------------------- /public/explorer/res/arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/arrow_down.png -------------------------------------------------------------------------------- /public/explorer/res/arrow_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/arrow_up.png -------------------------------------------------------------------------------- /public/explorer/res/arrowdown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/arrowdown.gif -------------------------------------------------------------------------------- /public/explorer/res/blueTriBig.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/blueTriBig.gif -------------------------------------------------------------------------------- /public/explorer/res/headerBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/headerBg.png -------------------------------------------------------------------------------- /public/explorer/res/lightning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/lightning.png -------------------------------------------------------------------------------- /public/explorer/res/lock_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/lock_add.png -------------------------------------------------------------------------------- /public/explorer/res/navPointer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/navPointer.gif -------------------------------------------------------------------------------- /public/explorer/res/navPointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/navPointer.png -------------------------------------------------------------------------------- /public/explorer/res/page_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/page_copy.png -------------------------------------------------------------------------------- /public/explorer/res/page_paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/page_paste.png -------------------------------------------------------------------------------- /public/explorer/res/persevere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/persevere.jpg -------------------------------------------------------------------------------- /public/explorer/res/persevere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/persevere.png -------------------------------------------------------------------------------- /public/explorer/res/selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/selected.png -------------------------------------------------------------------------------- /public/explorer/res/table_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/table_add.png -------------------------------------------------------------------------------- /public/explorer/res/blueTriSmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/blueTriSmall.png -------------------------------------------------------------------------------- /public/explorer/res/paste_plain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/paste_plain.png -------------------------------------------------------------------------------- /public/explorer/res/subContentBg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/subContentBg.png -------------------------------------------------------------------------------- /public/explorer/res/persevere-205.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/persevere-205.jpg -------------------------------------------------------------------------------- /public/explorer/res/persevere-273.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/persevere-273.jpg -------------------------------------------------------------------------------- /public/explorer/res/persevere-273.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/persevere-273.png -------------------------------------------------------------------------------- /public/explorer/res/persevere-glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/persevere-glow.png -------------------------------------------------------------------------------- /public/explorer/res/invert-persevere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/invert-persevere.png -------------------------------------------------------------------------------- /public/explorer/res/chart_organisation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/chart_organisation.png -------------------------------------------------------------------------------- /public/explorer/res/invert-persevere-273.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriszyp/persevere/master/public/explorer/res/invert-persevere-273.png -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Currently this is just a placeholder for potential future Persevere work. Please see http://www.persvr.org/ for the main Persevere site, or see the http://github.com/kriszyp/pintura for the Persevere 2.0 project called Pintura for running as a JSGI application. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "persevere", 3 | "author": "Kris Zyp", 4 | "dependencies": ["pintura"], 5 | "contributors": [], 6 | "keywords": [ 7 | "json", 8 | "database", 9 | "javascript", 10 | "persistence", 11 | "rest" 12 | ], 13 | "githubName": "persevere", 14 | "type": "zip", 15 | "location": "http://github.com/kriszyp/persevere/zipball/master" 16 | } -------------------------------------------------------------------------------- /public/explorer/res/iframeHistory.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/persvr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Some backwards compatible functions for Persevere 3 | */ 4 | 5 | var modelModule = require("./model"), 6 | Model = modelModule.Model; 7 | Class = function(schema){ 8 | return global[schema.id] = Model(schema.id, schema); 9 | }; 10 | 11 | load = function(id){ 12 | var parts = id.split("/"); 13 | var model = modelModule; 14 | for(var i = 1; i < parts.length - 1; i++){ 15 | model = model.get(parts[i]); 16 | } 17 | return model.get(parts[i]); 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /public/explorer/js/dojo-persevere/README.txt: -------------------------------------------------------------------------------- 1 | Persevere JavaScript Client 2 | 3 | The Persevere JavaScript Client provides an implementation of the Persistent JavaScript API through HTTP (RFC 2616) with JSON for data interaction with a server. 4 | 5 | This library is persevere.js and requires Dojo 1.2. To use the library: 6 | 7 | 8 | 9 | The documentation for the Persevere JavaScript Client can be found at: 10 | http://docs.persvr.org/persevere-javascript-client 11 | 12 | This package also includes a Login widget, implemented as a Dojo widget. The Login widget 13 | supports authenticating to Persevere and registering new users. -------------------------------------------------------------------------------- /public/explorer/js/dojo-persevere/resources/login/login.html: -------------------------------------------------------------------------------- 1 |

Enter Your Login Details

2 | 3 | 4 | 7 | 13 | 14 | 15 | 18 | 24 | 25 |
5 | 6 | 8 | 12 |
16 | 17 | 19 | 23 |
-------------------------------------------------------------------------------- /public/explorer/js/dojo-persevere/resources/login/register.html: -------------------------------------------------------------------------------- 1 |

Please Enter Your User Details:

2 | 3 | 4 | 7 | 14 | 15 | 16 | 19 | 26 | 27 | 28 | 31 | 38 | 39 |
5 | 6 | 8 | 13 |
17 | 18 | 20 | 25 |
29 | 30 | 32 | 37 |
-------------------------------------------------------------------------------- /public/explorer/js/dojo-persevere/LoginLink.js: -------------------------------------------------------------------------------- 1 | dojo.provide("persevere.LoginLink"); 2 | 3 | dojo.require("persevere.Login"); 4 | 5 | dojo.declare("persevere.LoginLink", [dijit._Widget, dijit._Templated], { 6 | templateString: 'Sign in', 7 | mustLogin: false, 8 | constructor: function(){ 9 | var plainXhr = dojo.xhr; 10 | var self = this; 11 | dojo.xhr = function(method,args,hasBody) { 12 | var dfd = plainXhr(method,args,hasBody); 13 | dfd.addBoth(function(){ 14 | persevere.username = dfd.ioArgs.xhr.getResponseHeader("Username"); 15 | dojo.addOnLoad(function(){ 16 | self.link.innerHTML = persevere.username ? "Sign out" : "Sign in"; 17 | if(!persevere.username && self.mustLogin && !self.showingLogin){ 18 | self.showLogin(); 19 | } 20 | }); 21 | }); 22 | return dfd; 23 | } 24 | }, 25 | onClick: function(){ 26 | if(persevere.username){ 27 | if(confirm("Are you sure you want to sign out?")){ 28 | dojo.xhrPost({ 29 | url: "Class/User", 30 | postData: dojo.toJson({method: "authenticate", id:"login", params:[null,null]}), 31 | handleAs: "json" 32 | }).addCallback(function(){ 33 | location.reload(); 34 | }); 35 | } 36 | }else{ 37 | this.showLogin(); 38 | } 39 | }, 40 | showLogin: function(){ 41 | this.showingLogin = true; 42 | var login = new persevere.Login({onLoginSuccess: function(){ 43 | location.reload(); 44 | }}); 45 | dojo.body().appendChild(login.domNode); 46 | login.startup(); 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /public/explorer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Persevere: The JSON database and JavaScript application server 7 | 8 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
Processing
45 | 46 |
47 |
48 | 51 | 52 | 58 |
59 |
60 |
61 |
62 |

Persevere Database Explorer

63 | Select a class/table:
64 |
65 |
66 |
67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /public/explorer/js/dojo-persevere/persevere.js: -------------------------------------------------------------------------------- 1 | dojo.provide("persevere.persevere"); 2 | dojo.require("dojox.rpc.JsonRest"); 3 | 4 | (function(){ 5 | var jr = dojox.rpc.JsonRest; 6 | var username = null; 7 | var plainXhr = dojo.xhr; 8 | dojo.xhr = function(method,args,hasBody) { 9 | dfd = plainXhr(method,args,hasBody); 10 | dfd.addCallback(function(){ 11 | username = dfd.ioArgs.xhr.getResponseHeader("Username"); 12 | }); 13 | return dfd; 14 | }; 15 | function lazyLoad(value, callback){ 16 | value._loadObject(function(result){ 17 | delete value._loadObject; 18 | callback(result); 19 | }); 20 | } 21 | pjs = { 22 | commit: function(callback){ 23 | jr.commit({onComplete:callback}); 24 | }, 25 | rollback: jr.revert, 26 | changing: jr.changing, 27 | 28 | get: function(/*Object*/ item, /*String*/property, callback){ 29 | // summary: 30 | // Gets the value of an item's 'property' 31 | // 32 | // item: 33 | // The item to get the value from 34 | // property: 35 | // property to look up value for 36 | // callback: 37 | // An optional callback to be called when get is finished if it is a lazy value 38 | var value = item[property]; 39 | if(callback){ 40 | if(value && value._loadObject){ 41 | lazyLoad(value, callback); 42 | }else{ 43 | callback(value); 44 | } 45 | } 46 | return value; 47 | }, 48 | set: function(object, property, value){ 49 | jr.changing(object); 50 | object[property]=value; 51 | }, 52 | getId: function(object){ 53 | return object.__id; 54 | }, 55 | load: function(id, callback){ 56 | if(id.match(/[^\/\w]|(\/$)/)){ 57 | // clear the cache if it is a query 58 | delete dojox.rpc.Rest._index[id]; 59 | } 60 | jr.fetch(id).addBoth(function(result){ 61 | callback(result); 62 | return result; 63 | }); 64 | }, 65 | remove: function(object){ 66 | jr.deleteObject(object); 67 | }, 68 | isPersisted: function(object){ 69 | return !!object.__id; 70 | }, 71 | getUserName: function(){ 72 | return username; 73 | }, 74 | loadClasses: function(/*String?*/path,/*Function?*/callback, /*Object*/scope){ 75 | // summary: 76 | // Loads the a set of classes/tables/schemas from the server 77 | // path: 78 | // URL of the Persevere server's root, this normally just "/" 79 | // which is the default value if the target is not provided 80 | // callback: 81 | // Allows the operation to happen asynchronously 82 | // scope: 83 | // An object that the returned classes will be defined in, as a namespace container 84 | // return: 85 | // A map/object of datastores. The name of each property is a the name of a store, 86 | // and the value is the actual data store object. 87 | path = (path && (path.match(/\/$/) ? path : (path + '/'))) || '/'; 88 | if(path.match(/^\w*:\/\//)){ 89 | // if it is cross-domain, we will use window.name for communication 90 | 91 | dojox.io.xhrWindowNamePlugin(path, dojox.io.xhrPlugins.fullHttpAdapter, true); 92 | } 93 | var rootService= dojox.rpc.Rest(path,true); 94 | var lastSync = dojox.rpc._sync; 95 | dojox.rpc._sync = !callback; 96 | var dfd = rootService("root");//dojo.xhrGet({url: target, sync:!callback, handleAs:'json'}); 97 | var results; 98 | //if no scope (namespace container) is provided, use window 99 | scope = scope || window; 100 | dfd.addBoth(function(schemas){ 101 | for(var i in schemas){ 102 | if(typeof schemas[i] == 'object'){ 103 | scope[i] = schemas[i] = dojox.rpc.JsonRest.getConstructor(new dojo._Url(path,i) + '/', schemas[i]); 104 | } 105 | } 106 | return (results = schemas); 107 | }); 108 | dojox.rpc._sync = lastSync; 109 | return callback ? dfd.addBoth(callback) : results; 110 | } 111 | }; 112 | })(); 113 | -------------------------------------------------------------------------------- /public/explorer/explorer.js: -------------------------------------------------------------------------------- 1 | dojo.addOnLoad(startExplorer); 2 | dojo.require("dojox.json.schema"); 3 | dojo.require("dojox.data.ClientFilter"); 4 | dojo.require("dojox.data.PersevereStore"); 5 | dojo.require("dojox.data.StoreExplorer"); 6 | 7 | function startExplorer(){ 8 | startExplorer = function(){}; // just run it once 9 | var plainXhr = dojo.xhr; 10 | var currentRequests = 0; 11 | username = undefined; 12 | var addSigninButton; 13 | dojo.xhr = function(method,args,hasBody) { 14 | if(!args.noStatus){ 15 | currentRequests++; 16 | var statusElement = dojo.byId("status"); 17 | if(statusElement){ 18 | dojo.style(statusElement,"display","block"); 19 | dojo.style(dojo.body(),"cursor","progress"); 20 | statusElement.innerHTML = method == "GET" ? "Loading" : "Processing"; 21 | } 22 | function done(res) { 23 | username = dfd.ioArgs.xhr.getResponseHeader("Username"); 24 | // if the sign-in button is waiting for us 25 | if(addSigninButton){ 26 | addSigninButton(); 27 | addSigninButton = null; 28 | } 29 | if(!--currentRequests){ 30 | if(statusElement){ 31 | dojo.style(statusElement,"display","none"); 32 | dojo.style(dojo.body(),"cursor","auto"); 33 | } 34 | } 35 | return res; 36 | } 37 | try { 38 | (args.headers = args.headers || {})['Include-ToString-Source'] = true; 39 | args.headers['Server-Methods'] = true; 40 | var dfd = plainXhr(method,args,hasBody); 41 | dfd.addBoth(done); 42 | } 43 | catch(e){ 44 | done(); 45 | } 46 | }else{ 47 | dfd = plainXhr(method,args,hasBody); 48 | } 49 | return dfd; 50 | } 51 | var path = location.pathname.match(/(.*\/)[^\/]*$/)[1]; 52 | var storesDfd = dojox.data.PersevereStore.getStores(path); // persevere stores are auto-generated 53 | storesDfd.addErrback(function(e){ 54 | if(/416/.test(e.message) && !username){ 55 | signin(); 56 | }else{ 57 | alert("Could not load Persevere classes (Class table)." + (/404/.test(e.message) ? " Are you sure you are connected to Persevere and not just a generic web server like Apache?" : "")); 58 | } 59 | }); 60 | var cp = new dijit.layout.ContentPane({ 61 | id: 'storeExplorer', 62 | region: 'center', 63 | style: "overflow:hidden" 64 | }).placeAt(dijit.byId("explorerSection")); 65 | var explorer = new dojox.data.StoreExplorer({ 66 | style:"height:100%;width:100%;border:1px solid black", 67 | stringQueries: true 68 | }); 69 | cp.attr("content", explorer); 70 | var defaultChildren = explorer.tree.model.getChildren; 71 | explorer.tree.model.getChildren = function(parentModelNode, onComplete){ 72 | var item = parentModelNode.value; 73 | if(((item.$ref || item.__id) + '').match(/\/$/)){ 74 | console.log("Please view the contents of this query by selecting the class in the explorer"); 75 | onComplete([]); 76 | return; 77 | } 78 | defaultChildren.apply(null,arguments); 79 | } 80 | var defaultCreateNew = explorer.createNew; 81 | explorer.createNew = function(){ 82 | if(activeClassName == 'Class'){ 83 | // special handling for classes 84 | var tableName = prompt("What would you like to name your new table/class?",""); 85 | if (!tableName) 86 | return; 87 | var superType = prompt("What table/class would you like to extend from (usually you want Object)?","Object"); 88 | if (!superType) 89 | return; 90 | dojo.rawXhrPost({ 91 | url: "Class/", 92 | sync: true, 93 | postData: dojo.toJson({id:tableName, "extends":{$ref:"../Class/" + superType}}) 94 | }); 95 | location.reload(); 96 | }else if(activeClassName == 'User'){ 97 | // special handling for users 98 | dojo.require("persevere.Login"); 99 | dojo.addOnLoad(function(){ 100 | var login = new persevere.Login({onLoginSuccess: function(){}}); 101 | login._showLogin = function(){};// do nothing when it tries to show the login 102 | dojo.body().appendChild(login.domNode); 103 | login._showRegister(); 104 | }); 105 | 106 | }else if(activeClassName == 'File'){ 107 | var fileDialog = this._fileDialog; 108 | if(fileDialog){ 109 | fileDialog.reset(); 110 | } 111 | else{ 112 | var uploadForm; 113 | onFileSelected = function(){ 114 | var uploadFrame = dojo.query("iframe", fileDialog.domNode)[0]; 115 | uploadFrame.onload = function(){ 116 | explorer.grid._refresh(); 117 | } 118 | uploadForm.submit(); 119 | dojo.query("span", fileDialog.domNode)[0].innerHTML = "Loading file..."; 120 | uploadForm.innerHTML = ""; 121 | fileDialog.hide(); 122 | }; 123 | this._fileDialog = fileDialog = new dijit.Dialog({ 124 | title: "Upload File", 125 | preload: true, 126 | content:'File Dialog' 127 | }); 128 | 129 | fileDialog.placeAt(dojo.body()); 130 | fileDialog.startup(); 131 | 132 | } 133 | fileDialog.domNode.innerHTML = 'Choose a file to upload
'; 134 | fileDialog.show(); 135 | uploadForm = dojo.query("form", fileDialog.domNode)[0]; 136 | } 137 | else{ 138 | defaultCreateNew.call(explorer); 139 | } 140 | } 141 | dojo.byId("queryText").innerHTML = 'JSONQuery:'; 142 | dojo.byId("queryTextBox").title="Enter a JSONQuery like [?name=\'foo\']"; 143 | var controlsArea = dojo.byId("queryText").parentNode; 144 | function addButton(name, action){ 145 | var button = new dijit.form.Button({label: name}).placeAt(controlsArea); 146 | button.onClick = action; 147 | return button; 148 | } 149 | addButton("Grant Access",function(){ 150 | //TODO: Maybe have two options, one for a 151 | var selectedItem = explorer.grid.selection.getSelected()[0]; 152 | var username = prompt("Who would you like to grant access to?","public"); 153 | if(username){ 154 | var accessLevel = prompt("What access level would you like to grant (none, limited, read, execute, append, write, or full)?","full"); 155 | if(accessLevel){ 156 | dojo.rawXhrPost({ 157 | url: "User/", 158 | postData: dojox.json.ref.toJson({ 159 | method:"grantAccess", 160 | params:[username, selectedItem || {__id:activeStore.target + '/'}, accessLevel], 161 | id:'' + Math.random() 162 | }) 163 | }); 164 | } 165 | } 166 | }); 167 | function signin(){ 168 | dojo.require("persevere.Login"); 169 | dojo.addOnLoad(function(){ 170 | var login = new persevere.Login({onLoginSuccess: function(){ 171 | location.reload(); 172 | }}); 173 | dojo.body().appendChild(login.domNode); 174 | login.startup(); 175 | }); 176 | } 177 | function createSignInButton(){ 178 | //creates the signin/signout button, now or later 179 | if(username){ 180 | addButton("Sign-out",function(){ 181 | if(confirm("Are you sure you want to sign out?")){ 182 | dojo.xhrPost({ 183 | url: "User/", 184 | postData: dojo.toJson({method: "authenticate", id:"login", user: null, password: null}), 185 | handleAs: "json" 186 | }).addCallback(function(){ 187 | location.reload(); 188 | }); 189 | } 190 | }); 191 | }else{ 192 | addButton("Sign-in",function(){ 193 | signin(); 194 | }); 195 | } 196 | } 197 | if(typeof username == 'undefined'){ 198 | // not ready yet 199 | addSigninButton = createSignInButton; 200 | }else{ 201 | createSignInButton(); 202 | } 203 | function showTable(className){ 204 | //dojo.byId("addItem").innerHTML = "New " + className; 205 | //dojo.byId("removeItem").innerHTML = "Delete " + className; 206 | //dojo.byId("query").value = ""; 207 | activeClassName = className; 208 | explorer.setItemName(className); 209 | explorer.setStore(activeStore = persevereStores[className]); 210 | } 211 | dojo.addOnLoad(function(){ 212 | storesDfd.addCallback(function(stores){ 213 | persevereStores = stores; 214 | var classes = dojo.byId("classes"); 215 | for(i in persevereStores){ 216 | var classOption = classes.appendChild(document.createElement("option")); 217 | classOption.innerHTML = i; 218 | classOption.value = i; 219 | } 220 | classes.value = "Class"; 221 | dojo.connect(classes,"change",function(){ 222 | showTable(classes.value); 223 | }); 224 | showTable("Class"); 225 | }); 226 | }); 227 | 228 | } 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /public/explorer/res/common.css: -------------------------------------------------------------------------------- 1 | /* ---------- reset ----------*/ 2 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, 3 | small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { 4 | margin: 0; 5 | padding: 0; 6 | border: 0; 7 | outline: 0; 8 | font-weight: inherit; 9 | font-style: inherit; 10 | list-style: none; 11 | font-size: 100%; 12 | font-family: inherit; 13 | vertical-align: baseline; 14 | } 15 | 16 | html, body { 17 | height: 100%; 18 | width: 100%; 19 | margin: 0; 20 | padding: 0; 21 | font-family: "Trebuchet MS", Verdana, Helvetica, sans-serif; 22 | color: #111; 23 | background-color:#333333; 24 | } 25 | p{ 26 | padding-top:5px; 27 | padding-bottom:5px; 28 | } 29 | 30 | h1 { 31 | font-weight:bold; 32 | font-size:1.2em; 33 | margin-bottom:1em; 34 | } 35 | 36 | /* ---------- header ----------*/ 37 | #headerContainer { 38 | background: url("headerBg.png") repeat-x top left; 39 | width: 100%; 40 | height: 116px; 41 | border-top: 1px solid #CFCFCF; 42 | border-bottom: 1px solid #000; 43 | } 44 | 45 | #logo { 46 | position:absolute; 47 | background: url("persevere-glow.png") no-repeat top left; 48 | width: 470px; 49 | height: 55px; 50 | left:0px; 51 | margin-top:10px; 52 | margin-left:126px; 53 | } 54 | 55 | /* ---------- navigation ----------*/ 56 | #mainNav { 57 | position: absolute; 58 | right: 0px; 59 | font-size: 14px; 60 | margin: 35px 126px 0 0; 61 | } 62 | 63 | #headerContainer #subNav { 64 | margin-top:76px; 65 | } 66 | 67 | #subNav { 68 | font-size: 14px; 69 | } 70 | 71 | #mainNav li, 72 | #subNav li { 73 | float: left; 74 | } 75 | 76 | #mainNav li a, 77 | #subNav li a { 78 | float:left; 79 | font-family: "Verdana","Arial","Helvetica",sans-serif; 80 | margin-left: 34px; 81 | padding-bottom: 23px; 82 | } 83 | 84 | .project{ 85 | height:100%; 86 | width:100%; 87 | } 88 | 89 | .project #projectsLink, .contact #contactLink, .about #aboutLink { 90 | background: url("navPointer.png") no-repeat left bottom; 91 | } 92 | 93 | .project #projectsLink { 94 | background-position: 65% 24px; 95 | } 96 | 97 | .about #aboutLink { 98 | background-position: 80% 24px; 99 | } 100 | 101 | .contact #contactLink { 102 | background-position: 78% 24px; 103 | } 104 | 105 | .dj_ie6 .project #projectsLink, .dj_ie6 .contact #contactLink, .dj_ie6 .about #aboutLink { 106 | background: url("navPointer.gif") no-repeat bottom center; 107 | } 108 | 109 | #mainNav a, 110 | #subNav a { 111 | color: #EFEFEF; 112 | text-decoration: none; 113 | } 114 | 115 | #mainNav a:hover, 116 | #subNav a:hover { 117 | color: #FFF; 118 | } 119 | 120 | #subNav { 121 | margin: 0 0 0 126px; 122 | font-size: 12px; 123 | } 124 | 125 | #subNav li { 126 | margin: 8px 31px 0 0; 127 | } 128 | 129 | #subNav li a { 130 | margin: 0; 131 | } 132 | 133 | #subNav .active { 134 | background: url("subNavR.png") no-repeat right top; 135 | height: 25px; 136 | padding-right: 10px; 137 | } 138 | 139 | #subNav .active a { 140 | background: url("subNavL.png") no-repeat left top; 141 | color: #444; 142 | margin-left: 0; 143 | padding-left: 10px; 144 | height: 25px; 145 | } 146 | 147 | #subNav li a { 148 | padding: 4px 0 0 0; 149 | } 150 | 151 | /* ---------- content ----------*/ 152 | #content { 153 | background: #BBCED4 url("bodyBg.png") repeat-x top left; 154 | width: 100%; 155 | height: auto; 156 | border-top: 1px solid #FFF; 157 | border-bottom: 1px solid #CECECE; 158 | font-size: 12px; 159 | } 160 | 161 | #contentOverlay { 162 | background: url("bodyImg.png") no-repeat top left; 163 | padding: 50px 0 50px 0; 164 | } 165 | 166 | /* ---------- sub content ----------*/ 167 | #subContent { 168 | background: #eef2f3 url("bodyBg.png") repeat-x top left; 169 | width: 100%; 170 | min-height: 250px; 171 | height:auto !important; 172 | height: 250px; 173 | border-top: 1px solid #FFF; 174 | } 175 | 176 | #subContent #subContentOverlay { 177 | margin: 36px 20px 20px; 178 | padding-bottom: 50px; 179 | padding-left: 105px; 180 | font-size:80%; 181 | width:800px; 182 | } 183 | 184 | #subContent #subContentL h2 { 185 | margin-left: 141px; 186 | margin-right: 10px; 187 | } 188 | 189 | #subContent h2 { 190 | background: #E6E6FF; 191 | height: 16px; 192 | color: #7D3333; 193 | font-size: 16px; 194 | font-weight: normal; 195 | padding: 8px; 196 | font-family: "Myriad Pro", "Trebuchet MS", Verdana, Helvetica, sans-serif; 197 | -moz-border-radius: 7px; 198 | -webkit-border-radius: 7px; 199 | border: 1px solid #E1E1E1; 200 | 201 | margin: 10px; 202 | width:300px; 203 | 204 | } 205 | 206 | #subContent h3 { 207 | height: 16px; 208 | margin: 15px; 209 | color: #515152; 210 | font-size: 14px; 211 | font-weight: bold; 212 | font-family: Arial, Verdana, Helvetica, sans-serif; 213 | } 214 | 215 | #subContent li, #mainContent ul li { 216 | background: url("blueTriSmall.png") no-repeat 0px 7px; 217 | display: block; 218 | line-height: 20px; 219 | margin-left: 1em; 220 | padding-left: 1em; 221 | font-size: 12px; 222 | } 223 | #explorerSection { 224 | margin: 20px; 225 | font-size: 80%; 226 | } 227 | 228 | #mainContent ol li { 229 | list-style:decimal; 230 | margin-left: 2em; 231 | padding-left: 0.3em; 232 | } 233 | 234 | 235 | #mainContent { 236 | overflow:hidden; 237 | width:100%; 238 | } 239 | 240 | #mainScroller { 241 | width:900%; 242 | } 243 | 244 | .contentBox { 245 | margin-left:126px; 246 | margin-right:2%; 247 | width:11%; 248 | float:left; 249 | } 250 | 251 | .contentBoxLogo { 252 | float: left; 253 | padding-right: 50px; 254 | width: 170px; 255 | } 256 | 257 | .contentBoxContent { 258 | float: left; 259 | width: 60%; 260 | } 261 | 262 | #subContent #subContentL #contributorPager { 263 | float:right; 264 | padding-top:3px; 265 | } 266 | 267 | #subContent #subContentL #contributorPager li { 268 | background: url("dot.gif") no-repeat; 269 | width:10px; 270 | height:10px; 271 | float:left; 272 | padding:0px; 273 | margin:0px; 274 | cursor:pointer; 275 | } 276 | 277 | #subContent #subContentL #contributorPager li.active { 278 | background:url("dotActive.gif") no-repeat; 279 | } 280 | 281 | #contributors { 282 | position:relative; 283 | margin-left:150px; 284 | margin-top:40px; 285 | height:90px; 286 | } 287 | 288 | .contributorBox { 289 | position:absolute; 290 | top:0; 291 | vertical-align:top; 292 | } 293 | 294 | .contributorBox img { 295 | vertical-align:middle; 296 | margin:10px; 297 | } 298 | 299 | .contributorBoxSelected { 300 | z-index:1; 301 | } 302 | 303 | .moreLink { 304 | color:#0093C8; 305 | display:block; 306 | float:right; 307 | font-size:12px; 308 | margin-top: 10px; 309 | margin-right: 20px; 310 | text-decoration:none; 311 | background:url("blueTriBig.gif") no-repeat 100% 70%; 312 | padding-right: 14px; 313 | } 314 | 315 | .projectLink { 316 | display:block; 317 | width:143px; 318 | background:url("linkSite.png") no-repeat; 319 | height:25px; 320 | margin-top:10px; 321 | text-decoration:none; 322 | } 323 | 324 | #mainContent p { 325 | padding-top: 5px; 326 | padding-bottom: 5px; 327 | } 328 | 329 | #mainContent h2 { 330 | padding-top: 10px; 331 | padding-bottom: 10px; 332 | font-weight: bold; 333 | } 334 | 335 | #mainContent a, 336 | #subContent a { 337 | color:#0093C8; 338 | text-decoration:none; 339 | } 340 | 341 | .boardMember { 342 | width: 100%; 343 | float: left; 344 | margin-bottom: 30px; 345 | } 346 | 347 | .boardMember img { 348 | float: left; 349 | padding: 7px; 350 | background: #FFF; 351 | border: 1px solid #ABABAB; 352 | } 353 | 354 | #mainContent .boardMember h2 { 355 | padding-top: 0; 356 | } 357 | 358 | .boardMember h2, 359 | .boardMember p { 360 | float: left; 361 | margin-left: 15px; 362 | width: 60%; 363 | } 364 | p { 365 | line-height:1.2em; 366 | } 367 | pre { 368 | border: 1px solid #ddd; 369 | padding: 1em; 370 | font-family: Courier; 371 | background-color:#eed; 372 | } 373 | #table-of-contents{ 374 | overflow: auto; 375 | width:200px; 376 | top:120px; 377 | left:840px; 378 | position:fixed; 379 | bottom:1px; 380 | background-color:#E6E6FF; 381 | border: 1px solid #555; 382 | } 383 | #table-of-contents ul{ 384 | padding-left:10px; 385 | margin-left:1px; 386 | } 387 | #aboutSection, #gettingStartedSection { 388 | padding-left:105px; 389 | } 390 | .details{ 391 | width:100px; 392 | height:40px; 393 | background: url("../jsclient/dijit/themes/tundra/images/spriteArrows.gif") no-repeat top left; 394 | } 395 | .dojoxGridCell{ 396 | font-size: 1.19em; 397 | } 398 | .expandable {padding-left: 20px;background: url('expand.gif') no-repeat 3px 3px;cursor: pointer;} 399 | .expanded {background: url('down.gif') no-repeat 3px 3px;padding-bottom:1px;padding-right: 1px;} 400 | .simple {padding-left: 20px;} 401 | textarea {} 402 | .pngImg {width:16px;height:16px;} 403 | .dropDownOptions {width: 19px;height: 21px;background: url('arrowdown.gif'); background-repeat: no-repeat;} 404 | .selected, .selected .tree {border: 1px solid blue;} // background: url('selected.png') repeat-x 405 | .field {width: 30%;display: block;float: left;} 406 | .objecttab {padding-left: 5px;padding-right: 5px;background-color: #ccc;padding-bottom: 1px;} 407 | .children {border: 1px solid #ccc;} 408 | .tree{overflow:auto; border: 1px solid #aaa} 409 | #status { 410 | position:absolute; 411 | top:0px; 412 | left:0px; 413 | z-index:10; 414 | background:url('spinner.gif') white no-repeat; 415 | border:1px solid black; 416 | padding:5px; 417 | padding-left:25px; 418 | font-family:arial; 419 | font-size: 80%; 420 | } 421 | 422 | -------------------------------------------------------------------------------- /public/explorer/js/dojo-persevere/Login.js: -------------------------------------------------------------------------------- 1 | dojo.provide("persevere.Login"); 2 | 3 | dojo.require("dijit.form.ValidationTextBox"); 4 | dojo.require("dijit.form.Button"); 5 | dojo.require("dijit.ProgressBar"); 6 | dojo.require("dijit.Dialog"); 7 | 8 | dojo.declare("persevere.Login", dijit.Dialog, 9 | { 10 | // summary: A login/register Dialog for a persevere server 11 | // 12 | // description: 13 | // Displays a dialog for logging into a persevere server and also allows new users 14 | // to register. You can provide your own login and register forms via 'loginHref' 15 | // and 'registerHref'. 'Login' and 'Register' buttons will be automatically added 16 | // to the login dialog and 'Register' and 'Cancel' buttons will be automatically 17 | // added to the register dialog. 18 | // 19 | // example: 20 | // |
21 | // 22 | // example: 23 | // | var foo = new dojox.data.persevere.Login({userUrl: "/User/"}); 24 | // | dojo.body().appendChild(foo.domNode); 25 | // | foo.startup(); 26 | 27 | // TODO: need to try to make this so that you can only have one instance per page. 28 | // consider disabling the close (x) button on the dialog. 29 | 30 | // currentUser: Object 31 | // The last user to be logged in via this widget. 32 | currentUser: null, 33 | 34 | // userUrl: String 35 | // The url to the 'User' class. This url is used to authenticate 36 | // and create users. 37 | userUrl: 'User/', 38 | 39 | // loginHref: String 40 | // The url to be used as the href to the login form. 41 | loginHref: dojo.moduleUrl("persevere", 'resources/login/login.html'), 42 | 43 | // loginMethod: String 44 | // The name of the method belonging to the Class at 'userUrl' that authenticates a user 45 | loginMethod: "authenticate", 46 | 47 | // registerHref: String 48 | // The url to be used as the href to the form used to register a new user. 49 | registerHref: dojo.moduleUrl("persevere", 'resources/login/register.html'), 50 | 51 | // registerMethod: String 52 | // The name of the method belonging to the Class at 'userUrl' that creates a new user 53 | registerMethod: "createUser", 54 | 55 | // userField: String 56 | // The name of the username field in the form at 'loginHref'. 57 | userField: 'username', 58 | 59 | // passField: String 60 | // The name of the password field in the form at 'loginHref'. 61 | passField: 'password', 62 | 63 | // newUserField: String 64 | // The name of the new username field in the form at 'registerHref'. 65 | newUserField: 'newUsername', 66 | 67 | // newPassField: String 68 | // The name of the new password field in the form at 'registerHref'. 69 | newPassField: 'newPassword', 70 | 71 | // confirmPassField: String 72 | // The name of the confirm password field in the form at 'registerHref'. 73 | confirmPassField: 'confirmPassword', 74 | 75 | _loginTitle: 'Login', 76 | _registerTitle: 'Add a new User', 77 | 78 | // how long the cookie should last, set to null for session-only authentication, 79 | // defaults to 10 days 80 | cookieLength: 864000000, 81 | 82 | constructor: function(options){ 83 | dojo.mixin(this, options); 84 | // a progress bar to show that we're waiting on a response from the server 85 | this._busy = new dijit.ProgressBar({style:"display:none", indeterminate:true}); 86 | // a place to put messages such as the error response from the server 87 | this._message = dojo.doc.createElement("div"); 88 | dojo.query(this._message).style({color:"red", wordWrap:"break-word", width:"220px", margin:"auto"}); 89 | // connect the ENTER key to the _onSubmit 90 | dojo.connect(this.domNode, "onkeypress", this, function(e){ 91 | if(e.charOrCode == dojo.keys.ENTER){ 92 | this._onSubmit(); 93 | } 94 | }); 95 | }, 96 | 97 | startup: function(){ 98 | this._showLogin(); 99 | }, 100 | 101 | _showLogin: function(){ 102 | // summary: shows the login dialog 103 | this._message.innerHTML = ''; 104 | var footer = dojo.doc.createElement('div'); 105 | var buttons = dojo.doc.createElement('div'); 106 | // need to find a better way to right-align the buttons - using dir:rtl and tabindex is not ideal 107 | buttons.setAttribute("dir", "rtl"); 108 | var registerButton = new dijit.form.Button({type: "reset", label: "Register", tabIndex: "-1"}).placeAt(buttons); 109 | registerButton.onClick = dojo.hitch(this, "_showRegister"); 110 | var loginButton = new dijit.form.Button({type: "submit", label: "Login"}).placeAt(buttons); 111 | footer.appendChild(buttons); 112 | footer.appendChild(this._message); 113 | this._busy.placeAt(footer); 114 | this.onDownloadEnd = function(){ 115 | this.containerNode.appendChild(footer); 116 | }; 117 | this.execute = this._login; 118 | this.attr("href", this.loginHref); 119 | this.attr("title", this._loginTitle); 120 | this.show(); 121 | }, 122 | 123 | _showRegister: function(){ 124 | // summary: shows the register dialog 125 | this._message.innerHTML = ''; 126 | var footer = dojo.doc.createElement('div'); 127 | var buttons = dojo.doc.createElement('div'); 128 | buttons.setAttribute("dir", "rtl"); 129 | var cancelButton = new dijit.form.Button({type: "reset", label: "Cancel", tabIndex: "-1"}).placeAt(buttons); 130 | cancelButton.onClick = dojo.hitch(this, "_showLogin"); 131 | var submitButton = new dijit.form.Button({type: "submit", label: "Register"}).placeAt(buttons); 132 | footer.appendChild(buttons); 133 | footer.appendChild(this._message); 134 | this._busy.placeAt(footer); 135 | this.onDownloadEnd = function(){ 136 | this.containerNode.appendChild(footer); 137 | }; 138 | this.execute = this._register; 139 | this.attr("href", this.registerHref); 140 | this.attr("title", this._registerTitle); 141 | this.show(); 142 | }, 143 | 144 | _login: function(form){ 145 | // summary: this is the login action - it attempts to authenticate the user 146 | // using the "authenticate" method of the Class at 'userUrl' using the form params 147 | if (this.validate()){ 148 | this._busy.attr("style", "display:block"); 149 | dojo.xhrPost({ 150 | url: this.userUrl, 151 | postData: dojo.toJson({method: this.loginMethod, id:"login", user: form[this.userField], password: form[this.passField], expires: this.cookieLength && new Date(new Date().getTime() + this.cookieLength).toGMTString()}), 152 | handleAs: "json", 153 | headers: {Accept:"application/javascript, application/json"}, 154 | load: dojo.hitch(this, "_loginLoad"), 155 | error: dojo.hitch(this, "_loginError") 156 | }); 157 | } else { 158 | this._message.innerHTML = 'Input is not valid'; 159 | this.show(); 160 | } 161 | }, 162 | 163 | _loginLoad: function(response, request){ 164 | // summary: handles the response to the authentication attempt 165 | this._busy.attr("style", "display:none"); 166 | if (response.error != null){ 167 | this._message.innerHTML = response.error; 168 | this.onLoginFail(response, request); 169 | } else { 170 | this.currentUser = response.result; 171 | this.onLoginSuccess(response, request); 172 | } 173 | return response; 174 | }, 175 | 176 | _loginError: function(response, request){ 177 | // summary: handles login errors 178 | this._busy.attr("style", "display:none"); 179 | this.onLoginError(response, request); 180 | return response; 181 | }, 182 | 183 | _register: function(form){ 184 | // summary: this is the register action - it attempts to register a new user 185 | // using the 'registerMethod' of the Class at the 'userUrl' 186 | if (this.validate() && (form[this.newPassField] == form[this.confirmPassField])){ 187 | this._busy.attr("style", "display:block"); 188 | dojo.xhrPost({ 189 | url: this.userUrl, 190 | postData: dojo.toJson({method: this.registerMethod,id:"register", params:[form[this.newUserField], form[this.newPassField]]}), 191 | handleAs: "json", 192 | load: dojo.hitch(this, "_registerLoad"), 193 | error: dojo.hitch(this, "_registerError") 194 | }) 195 | } else { 196 | this._message.innerHTML = 'Input is not valid'; 197 | this.show(); 198 | } 199 | }, 200 | 201 | _registerLoad: function(response, request){ 202 | this._busy.attr("style", "display:none"); 203 | if (response.error != null){ 204 | this._message.innerHTML = response.error; 205 | this.onRegisterFail(response, request); 206 | } else { 207 | this.onRegisterSuccess(response, request); 208 | } 209 | return response; 210 | }, 211 | 212 | _registerError: function(response, request){ 213 | this._busy.attr("style", "display:none"); 214 | this.onRegisterError(response, request); 215 | return response; 216 | }, 217 | 218 | login: function(){ 219 | this._showLogin(); 220 | }, 221 | 222 | logout: function(callBack){ 223 | if (confirm("Are you sure you want to sign out?")) { 224 | dojo.xhrPost({ 225 | url: this.userUrl, 226 | postData: dojo.toJson({ 227 | method: this.loginMethod, 228 | id: "logout", 229 | user: null, 230 | password: null 231 | }), 232 | handleAs: "json", 233 | handle: function(response, request){ 234 | this.currentUser = null; 235 | } 236 | }).addCallback(callBack); 237 | } 238 | }, 239 | 240 | onLoginSuccess: function(response, request){ 241 | }, 242 | 243 | onLoginFail: function(response, request){ 244 | this.show(); 245 | }, 246 | 247 | onLoginError: function(response, request){ 248 | console.error("HTTP status code: ", request.xhr.status); 249 | }, 250 | 251 | onRegisterSuccess: function(response, request){ 252 | // change this to authenticate the user and add the rest of the 253 | // register params to the new user object 254 | /* var form = this.attr("value"); 255 | var user = response.result; 256 | for (var attr in form){ 257 | if (!user[attr] && (attr != this.newUserField) && (attr != this.newPassField) && (attr != this.confirmPassField)){ 258 | user[attr] = form[attr]; 259 | } 260 | } 261 | var login = dojo.xhrPost({ 262 | url: this.userUrl, 263 | postData: dojo.toJson({method: this.loginMethod, id:"login", params:[form[this.newUserField], form[this.newPassField]]}), 264 | handleAs: "json", 265 | handle: dojo.hitch(this, "_loginHandle"), 266 | error: dojo.hitch(this, "_loginError") 267 | }); 268 | var call = { 269 | url: this.userUrl + response.result.id 270 | // pick up from here... - need to add the other fields to the user object. 271 | } 272 | login.addCallback(dojo.hitch(this, function(){ 273 | dojo.xhrPut({ 274 | url: this.userUrl + response.result.id 275 | }) 276 | }));*/ 277 | this._showLogin(); 278 | this._message.innerHTML = 'Registration successful. Please login.' 279 | }, 280 | 281 | onRegisterFail: function(response, request){ 282 | this.show(); 283 | }, 284 | 285 | onRegisterError: function(response, request){ 286 | console.error("HTTP status code: ", request.xhr.status); 287 | } 288 | }); 289 | -------------------------------------------------------------------------------- /lib/json-query.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module provides querying functionality, with a JSONQuery implementation 3 | * and a helper class for parsing JSONQueries 4 | */ 5 | 6 | // The main function for executing JSONQueries 7 | exports.jsonQuery = jsonQuery; 8 | 9 | // its easier to write (and read) these as regular expressions (since that is how they are used) 10 | var $prop = /(?:@?\.?([a-zA-Z_$][\w_$]*))/.source; 11 | var $value = /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\/[\w_$]+\/[\w]+|\-?[0-9]+(?:\.[0-9]+)?(?:[eE]\-?[0-9]+)?|true(?![\w])|false(?![\w])|null(?![\w]))/.source; 12 | var $comparator = /(===|!==|==|!=|>=|<=|=|<|>)/.source; 13 | var $operator = /([\+\-\*\/\&\|\?\:])/.source; 14 | var $logic = /([\&\|])/.source; 15 | var $expression = /((?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\[(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\]])*\]|[^\]])*)/.source; 16 | var $filter = "(?:\\[?\\?" + $expression + "\\]?)"; 17 | var $sort = "(?:\\[[\\\\\\/]" + $expression + "\\])"; 18 | exports.QueryRegExp = QueryRegExp; 19 | function QueryRegExp(regexp){ 20 | return new RegExp((regexp.source || regexp).replace(/\$[a-z]+/g, function(t){ 21 | switch(t){ 22 | case "$prop" : return $prop; 23 | case "$value" : return $value; 24 | case "$comparator" : return $comparator; 25 | case "$operator" : return $operator; 26 | case "$logic" : return $logic; 27 | case "$expression" : return $expression; 28 | case "$filter" : return $filter; 29 | case "$sort" : return $sort; 30 | } 31 | return t; 32 | }), (regexp.global ? "g" : "") + (regexp.ignoreCase ? "m" : "")); 33 | } 34 | function throwMaxIterations(){ 35 | throw new Error("Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed."); 36 | } 37 | exports.maxIterations = 10000; 38 | function stringToValue(string){ 39 | switch(string){ 40 | case "true": return true; 41 | case "false": return false; 42 | case "null": return null; 43 | default: 44 | var number = parseFloat(string, 10); 45 | if(isNaN(number)){ 46 | if(exports.compatible){ 47 | if(string.charAt(0) == "'" && string.charAt(string.length-1) == "'"){ 48 | return JSON.parse('"' + string.substring(1,string.length-1) + '"'); 49 | } 50 | } 51 | return string; 52 | } 53 | return number; 54 | } 55 | 56 | }; 57 | function convertComparator(comparator){ 58 | switch(comparator){ 59 | case "-+" : return "<"; 60 | case "+-" : return ">"; 61 | case "+=-" : return "=>"; 62 | case "-=+" : return "=<"; 63 | } 64 | return comparator; 65 | } 66 | exports.compatible = true; 67 | exports.parseQuery = function(/*String*/query){ 68 | if(exports.compatible){ 69 | query = query.replace(/%3C=/g,"-=+").replace(/%3E=/g,"+=-").replace(/%3C/g,"-+").replace(/%3E/g,"+-"); 70 | } 71 | var ast = {children:[], type:"group"}; 72 | var originalAst = ast; 73 | query.replace(/(([\w%\._]+)([\-=\+!]+)([\+\-\w%\._]+))|([&\|,\)])|(([\+\-\w%\._]*)(\(?))/g, 74 | function(t, expression, name, comparator, value, token, callOrGroup, methodOrValue, openParan){ 75 | if(expression){ 76 | comparison = { 77 | type:"comparison", 78 | comparator: convertComparator(comparator), 79 | name: decodeURIComponent(name), 80 | value: stringToValue(decodeURIComponent(value)) 81 | }; 82 | ast.children.push(comparison); 83 | } 84 | else if(callOrGroup){ 85 | if(openParan){ 86 | var newAst = {type:"group", children:[]}; 87 | ast.children.push(newAst); 88 | newAst.parent = ast; 89 | ast = newAst; 90 | if(methodOrValue){ 91 | ast.type = "call"; 92 | ast.method = methodOrValue; 93 | } 94 | } 95 | else{ 96 | ast.children.push(stringToValue(methodOrValue)); 97 | } 98 | } 99 | else if(token){ 100 | switch(token){ 101 | case ')' : 102 | ast = ast.parent; 103 | break; 104 | case '&' : case '|': 105 | if(!ast.logic){ 106 | ast.logic = token; 107 | } 108 | else if(ast.logic != token){ 109 | var newAst = {type:"group", children:[]}; 110 | newAst.children.push(ast); 111 | if(ast.parent){ 112 | ast.parent.children[ast.parent.length-1] = newAst; 113 | } 114 | // set the parent to that because a real paran should go up to the real parent 115 | newAst.parent = ast.parent; 116 | ast = newAst; 117 | ast.logic = token; 118 | } 119 | } 120 | } 121 | }); 122 | ast.logic = ast.logic || "&"; 123 | return ast; 124 | /* 125 | var TOKEN = /\(|[\w%\._]+/g; 126 | var OPERATOR = /[-=+!]+|\(/g; 127 | var NEXT = /[&\|\)]/g; 128 | 129 | TOKEN.lastIndex = 0; 130 | function group(){ 131 | var ast = []; 132 | var match = TOKEN.exec(query); 133 | if(match === '('){ 134 | ast.push(group()); 135 | } 136 | else{ 137 | OPERATOR.lastIndex = TOKEN.lastIndex; 138 | var operator = OPERATOR.exec(query); 139 | var comparison = {}; 140 | ast.push(comparison); 141 | if(operator == '('){ 142 | comparison.type = "call"; 143 | comparison.parameters = 144 | } 145 | comparison.type = operator; 146 | 147 | } 148 | return ast; 149 | } 150 | return group();*/ 151 | } 152 | function executeQuery(query, options, target){ 153 | if(typeof query === "string"){ 154 | query = parseQuery(query); 155 | } 156 | var methods = options.methods || {}; 157 | if(!methods.sort){ 158 | methods.sort = function(sortAttribute){ 159 | var firstChar = sortAttribute.charAt(0); 160 | var ascending = true; 161 | if(firstChar == "-" || firstChar == "+"){ 162 | if(firstChar == "-"){ 163 | ascending = false; 164 | } 165 | sortAttribute = sortAttribute.substring(1); 166 | } 167 | this.sort(function(a, b){ 168 | return ascending == a[sortAttribute] > b[sortAttribute] ? 1 : -1; 169 | }); 170 | return this; 171 | } 172 | } 173 | var first = true; 174 | var js = ""; 175 | query.children.forEach(function(child){ 176 | if(child.type == "comparison"){ 177 | if(!options){ 178 | throw new Error("Values must be set as parameters on the options argument, which was not provided"); 179 | } 180 | if(first){ 181 | js += "target = target.filter(function(item){return "; 182 | first = false; 183 | } 184 | else{ 185 | js += query.logic + query.logic; 186 | } 187 | var index = (options.parameters = options.parameters || []).push(child.value); 188 | if(child.comparator == "="){ 189 | child.comparator = "=="; 190 | } 191 | js += "item." + child.name + child.comparator + "options.parameters[" + (index -1) + "]"; 192 | 193 | } 194 | if(!first){ 195 | js += "});"; 196 | } 197 | else if(child.type == "call"){ 198 | if(methods[child.method]){ 199 | var index = (options.parameters = options.parameters || []).push(child.children); 200 | js += "target = methods." + child.method + ".apply(target,options.parameters[" + (index -1) + "]);"; 201 | } 202 | else{ 203 | throw new URIError("Invalid JSON Query syntax, " + child.method + " not implemented"); 204 | } 205 | } 206 | else{ 207 | throw new URIError("Invalid JSON Query syntax, unknown type"); 208 | } 209 | }); 210 | return eval(js + "target;"); 211 | 212 | } 213 | 214 | function jsonQuery(/*String*/query, obj, args){ 215 | // summary: 216 | // Performs a JSONQuery on the provided object and returns the results. 217 | // If no object is provided (just a query), it returns a function that evaluates objects 218 | // according to the provided query. 219 | // query: 220 | // Query string 221 | // obj: 222 | // Target of the JSONQuery 223 | // description: 224 | // | dojox.json.query("foo",{foo:"bar"}) - > "bar" 225 | // | evaluator = dojox.json.query("?foo='bar'&rating>3"); 226 | query = query.toString(); 227 | var iterations = 0; 228 | // setup JSONQuery library 229 | function slice(obj,start,end,step){ 230 | var i, results = []; 231 | if(step < 5){ 232 | // do an iterator friendly 233 | i = 0; 234 | obj.forEach(function(item){ 235 | if(i >= end){ 236 | return results; 237 | } 238 | if(i >= start && i % step == 0){ 239 | results.push(item); 240 | } 241 | i++; 242 | if(i > exports.maxIterations){ 243 | throwMaxIterations(); 244 | } 245 | }); 246 | return results; 247 | } 248 | var len=obj.length; 249 | end = end || len; 250 | 251 | start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start); 252 | end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end); 253 | iterations += end - start; 254 | if(iterations > exports.maxIterations){ 255 | throwMaxIterations(); 256 | } 257 | for(i=start; i exports.maxIterations){ 277 | throwMaxIterations(); 278 | } 279 | if(!name){ 280 | // if we don't have a name we are just getting all the properties values (.* or [*]) 281 | results.push(val); 282 | }else if(val && typeof val == 'object'){ 283 | walk(val); 284 | } 285 | }); 286 | } 287 | if(name instanceof Array){ 288 | // this is called when multiple items are in the brackets: [3,4,5] 289 | if(name.length==1){ 290 | // this can happen as a result of the parser becoming confused about commas 291 | // in the brackets like [@.func(4,2)]. Fixing the parser would require recursive 292 | // analsys, very expensive, but this fixes the problem nicely. 293 | return obj[name[0]]; 294 | } 295 | for(var i = 0; i < name.length; i++){ 296 | if(iterations++ > exports.maxIterations){ 297 | throwMaxIterations(); 298 | } 299 | results.push(obj[name[i]]); 300 | } 301 | }else{ 302 | // otherwise we expanding 303 | walk(obj); 304 | } 305 | return results; 306 | } 307 | 308 | var _this = this; // persevere edit 309 | // persevere edit 310 | function filter(array,func){ 311 | if(array.filter){ 312 | return array.filter(func) 313 | } 314 | throw new Error("Can only filter on arrays, not objects"); 315 | } 316 | // TODO: need some way to mark this as a date-string for comparison at the DB level 317 | function date(dateValue){ 318 | return (arguments.length ? new Date(dateValue) : new Date()).getTime(); 319 | } 320 | function map(array,func){ 321 | return array.map(func); 322 | } 323 | var depth = 0; 324 | var str = []; 325 | var __ids__={}; 326 | var idNum = 0; 327 | query = query.replace(/\/([\w_$]+\/[\w]+)/,function(t,a){ 328 | // TODO: load the object first and then store it so it doesn't need to be loaded on each iteration 329 | // handle object id literals 330 | if(pjs.absolutePathPrefix && ('/' + a).substring(pjs.absolutePathPrefix)){ 331 | a = a.substring(pjs.absolutePathPrefix.length - 1); 332 | } 333 | __ids__['a' + idNum] = load(a); 334 | return "('" + a + "'&&__ids__.a" + (idNum++) + ')'; 335 | }). 336 | replace(/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g,function(t){ 337 | depth += t == '[' ? 1 : t == ']' ? -1 : 0; // keep track of bracket depth 338 | return (t == ']' && depth > 0) ? '`]' : // we mark all the inner brackets as skippable 339 | (t.charAt(0) == '"' || t.charAt(0) == "'") ? "`" + (str.push(t) - 1) :// and replace all the strings 340 | t; 341 | }); 342 | var prefix = ''; 343 | function call(name){ 344 | // creates a function call and puts the expression so far in a parameter for a call 345 | prefix = name + "(" + prefix; 346 | } 347 | function makeRegex(t,a,b,c,d,e,f,g){ 348 | // creates a regular expression matcher for when wildcards and ignore case is used 349 | return str[g].match(/[\*\?]/) || f == '~' ? 350 | "/^" + str[g].substring(1,str[g].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,".$1") + (f == '~' ? '$/i' : '$/') + ".test(" + a + ")" : 351 | t; 352 | } 353 | query.replace(/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/,function(){ 354 | throw new Error("Unsafe function call"); 355 | }); 356 | 357 | query = query.replace(/([^=<>]=)([^=])/g,"$1=$2"). // change the equals to comparisons 358 | replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(t){ 359 | return t.charAt(0) == '.' ? t : // leave .prop alone 360 | t == '@' ? "$obj" :// the reference to the current object 361 | //Persevere edit 362 | (t.match(/:|^(\$|Math|args|true|false|null|instanceof|date|__ids__)$/) ? "" : "$obj.") + t; // plain names should be properties of root... unless they are a label in object initializer 363 | }). 364 | replace(/\.?\.?\[(`\]|[^\]])*\]|[\(\,]?\?.*|\.\.([\w\$_]+)|\.\*/g,function(t,a,b){ 365 | var oper = t.match(/^\.?\.?(\[\s*\?|\?|\[\s*==)(.*?)\]?$/); // [?expr] and ?expr and [=expr and =expr 366 | if(oper){ 367 | var prefix = ''; 368 | if(t.match(/^\./)){ 369 | // recursive object search 370 | call("expand"); 371 | prefix = ",true)"; 372 | } 373 | call(oper[1].match(/\=/) ? "map" : "filter");//persevere edit 374 | return prefix + ",function($obj){try{return " + oper[2] + "}catch(e){}})"; 375 | } 376 | oper = t.match(/^\[\s*([\/\\].*)\]/); // [/sortexpr,\sortexpr] 377 | if(oper){ 378 | // make a copy of the array and then sort it using the sorting expression 379 | var first = true; 380 | return ".sort(" + oper[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(t,a,b){ 381 | return (first ? (first=false) || '': ',') + "function($obj){return " + b + "}," + (a== "/"); 382 | }) + ")"; 383 | } 384 | oper = t.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/); // slice [0:3] 385 | if(oper){ 386 | call("slice"); 387 | return "," + (oper[1] || 0) + "," + (oper[2] || 0) + "," + (oper[3] || 1) + ")"; 388 | } 389 | if(t.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){ // ..prop and [*] 390 | call("expand"); 391 | return (t.charAt(1) == '.' ? 392 | ",'" + b + "'" : // ..prop 393 | t.match(/,/) ? 394 | "," + t : // [prop1,prop2] 395 | "") + ")"; // [*] 396 | } 397 | return t; 398 | }). 399 | replace(/([\(\,])\?(((\([^\)]*\))|[^\),])*)([\),])/,function(t,a,b,c,d,e){ 400 | // handle func(?expr) 401 | return a + "function($obj){try{return " + b + "}catch(e){}}" + e; 402 | }). 403 | replace(/\.(class|this|null|function|extends|instanceof|var)(\W)/,function(t,a,b){ 404 | // fix reserved words 405 | return '["' + a + '"]' + b; 406 | }). 407 | replace(/(\$obj\s*((\.\s*[\w_$]+\s*)|(\[\s*`([0-9]+)\s*`\]))*)(==|~)\s*`([0-9]+)/g,makeRegex). // create regex matching 408 | replace(/`([0-9]+)\s*(==|~)\s*(\$obj\s*((\.\s*[\w_$]+)|(\[\s*`([0-9]+)\s*`\]))*)/g,function(t,a,b,c,d,e,f,g){ // and do it for reverse = 409 | return makeRegex(t,c,d,e,f,g,b,a); 410 | }); 411 | query = prefix + (query.charAt(0) == '$' ? "" : "$") + query.replace(/`([0-9]+|\])/g,function(t,a){ 412 | //restore the strings 413 | return a == ']' ? ']' : str[a]; 414 | }); 415 | // create a function within this scope (so it can use expand and slice) 416 | 417 | var executor = eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return " + query + "}"); 418 | 419 | for(var i = 0;i