Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit a34318dbb3b9f3d57bed0f842daa8a8daf3cbdda 0 parents
@kriszyp kriszyp authored
11 README.md
@@ -0,0 +1,11 @@
+This is an example Wiki built with Persevere 2.0. It is recommended that you
+use [Nodules](http://github.com/kriszyp/nodules) on Node to run this example so that
+all dependencies will be automatically resolved, or you can also use the nightly
+build that bundles with Narwhal. Once installed, you can download this package and from
+the root folder simply run:
+
+ node /path/to/nodules
+
+or for Narwhal:
+
+ jackup
1  bin/README.md
@@ -0,0 +1 @@
+These are the scripts for running from Narwhal
50 bin/activate
@@ -0,0 +1,50 @@
+
+if [ -z "${BASH_ARGV[0]}" ]; then
+
+ # as a last recourse, use the present working directory
+ PACKAGE_HOME=$(pwd)
+
+else
+
+ # get the absolute path of the executable
+ SELF_PATH=$(
+ cd -P -- "$(dirname -- "${BASH_ARGV[0]}")" \
+ && pwd -P
+ ) && SELF_PATH=$SELF_PATH/$(basename -- "${BASH_ARGV[0]}")
+
+ # resolve symlinks
+ while [ -h "$SELF_PATH" ]; do
+ DIR=$(dirname -- "$SELF_PATH")
+ SYM=$(readlink -- "$SELF_PATH")
+ SELF_PATH=$(cd -- "$DIR" && cd -- $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
+ done
+
+ PACKAGE_HOME=$(dirname -- "$(dirname -- "$SELF_PATH")")
+
+fi
+
+export PACKAGE_HOME
+
+# which -s narwhal doesn't work (os x 10.5, kriskowal)
+if [ -f "$PACKAGE_HOME"/bin/narwhal ]; then
+ NARWHAL="$PACKAGE_HOME"/bin/narwhal
+elif [ -f "$PACKAGE_HOME"/packages/narwhal/bin/narwhal ]; then
+ NARWHAL="$PACKAGE_HOME"/packages/narwhal/bin/narwhal
+else
+ env narwhal -e '' >/dev/null 2>&1
+ if [ "$?" -ne 127 ]; then
+ NARWHAL=narwhal
+ else
+ echo "ERROR: narwhal is not in your PATH nor in $PACKAGE_HOME/bin." >&2
+ fi
+fi
+
+if [ -f "$PACKAGE_HOME"/narwhal.conf ]; then
+ source "$PACKAGE_HOME"/narwhal.conf
+ export NARWHAL_DEFAULT_ENGINE
+fi
+
+if [ "$NARWHAL" ]; then
+ export PATH="$("$NARWHAL" --package "$PACKAGE_HOME" --path :)"
+fi
+
50 bin/activate.bash
@@ -0,0 +1,50 @@
+
+if [ -z "${BASH_ARGV[0]}" ]; then
+
+ # as a last recourse, use the present working directory
+ PACKAGE_HOME=$(pwd)
+
+else
+
+ # get the absolute path of the executable
+ SELF_PATH=$(
+ cd -P -- "$(dirname -- "${BASH_ARGV[0]}")" \
+ && pwd -P
+ ) && SELF_PATH=$SELF_PATH/$(basename -- "${BASH_ARGV[0]}")
+
+ # resolve symlinks
+ while [ -h "$SELF_PATH" ]; do
+ DIR=$(dirname -- "$SELF_PATH")
+ SYM=$(readlink -- "$SELF_PATH")
+ SELF_PATH=$(cd -- "$DIR" && cd -- $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
+ done
+
+ PACKAGE_HOME=$(dirname -- "$(dirname -- "$SELF_PATH")")
+
+fi
+
+export PACKAGE_HOME
+
+# which -s narwhal doesn't work (os x 10.5, kriskowal)
+if [ -f "$PACKAGE_HOME"/bin/narwhal ]; then
+ NARWHAL="$PACKAGE_HOME"/bin/narwhal
+elif [ -f "$PACKAGE_HOME"/packages/narwhal/bin/narwhal ]; then
+ NARWHAL="$PACKAGE_HOME"/packages/narwhal/bin/narwhal
+else
+ env narwhal -e '' >/dev/null 2>&1
+ if [ "$?" -ne 127 ]; then
+ NARWHAL=narwhal
+ else
+ echo "ERROR: narwhal is not in your PATH nor in $PACKAGE_HOME/bin." >&2
+ fi
+fi
+
+if [ -f "$PACKAGE_HOME"/narwhal.conf ]; then
+ source "$PACKAGE_HOME"/narwhal.conf
+ export NARWHAL_DEFAULT_ENGINE
+fi
+
+if [ "$NARWHAL" ]; then
+ export PATH="$("$NARWHAL" --package "$PACKAGE_HOME" --path :)"
+fi
+
3  bin/activate.cmd
@@ -0,0 +1,3 @@
+@echo off
+
+set PATH=%~dp0;%PATH%
36 bin/sea
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+# this script is intended to work both in narwhal/bin and in
+# any project's bin dir; it's copied by tusk --init.
+
+# get the absolute path of the executable
+SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")
+
+# resolve symlinks
+while [ -h "$SELF_PATH" ]; do
+ DIR=$(dirname -- "$SELF_PATH")
+ SYM=$(readlink -- "$SELF_PATH")
+ SELF_PATH=$(cd -- "$DIR" && cd -- $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
+done
+
+export PACKAGE_HOME=$(dirname -- "$(dirname -- "$SELF_PATH")")
+
+source "$PACKAGE_HOME/bin/activate.bash"
+
+export OLDSEA="$SEA"
+export SEA="$PACKAGE_HOME"
+export SEALVL="$((SEALVL + 1))"
+
+if [ "$#" -lt 1 ]; then
+ echo PATH="$PATH" >&2
+ echo SEA="$SEA" >&2
+ echo SEALVL="$SEALVL" >&2
+ "$SHELL"
+else
+ "$SHELL" -c "$*"
+fi
+if [ "$OLDSEA" != "" ]; then
+ echo SEA="$OLDSEA" >&2
+fi
+echo SEALVL="$((SEALVL - 1))" >&2
+
25 bin/sea.cmd
@@ -0,0 +1,25 @@
+@echo off
+setlocal
+
+set SHELL=cmd.exe
+
+set PACKAGE_HOME=%~dp0\..
+
+call %PACKAGE_HOME%\bin\activate.cmd
+
+set OLDSEA=%SEA%
+set SEA=%PACKAGE_HOME%
+set /a SEALVL=%SEALVL% + 1
+
+if "%1" == "" (
+ echo SEALVL=%SEALVL%
+ echo SEA=%SEA%
+ echo PATH=%PATH%
+ %SHELL%
+) else (
+ %SHELL% %*
+)
+
+set /a SEALVL=%SEALVL% - 1
+echo SEALVL=%SEALVL%
+echo SEA=%OLDSEA%
1  data/README.md
@@ -0,0 +1 @@
+This is the default location for data files
48 jackconfig.js
@@ -0,0 +1,48 @@
+/**
+ * The starting point for Pintura running as a Jack app.
+ */
+try{
+ var pintura = require("pintura/pintura");
+}catch(e){
+ // old loaders need to use this type of access, pintura will fix things from there
+ pintura = require("pintura");
+}
+
+var File = require("file"),
+ transporter = require("pintura/jsgi/transporter");
+
+require("app");
+
+// setup the Jack application
+exports.app =
+ // this will provide module wrapping for the server side CommonJS libraries for the client
+ transporter.Transporter({},
+ // make the root url redirect to /Page/Root
+ require("pintura/jsgi/redirect-root").RedirectRoot(
+ // main Pintura handler
+ pintura.app
+ )
+ );
+
+// now setup the development environment, handle static files before reloading the app
+// for better performance
+exports.development = function(app, options){
+ return require("jack/cascade").Cascade([
+ // cascade from static to pintura REST handling
+/* // this will provide module wrapping for the Dojo modules for the client
+ transporter.Transporter({
+ urlPrefix:"/js/",
+ paths:["../../persevere/public/js/"],
+ converter: transporter.Dojo
+ }),*/
+ // the main place for static files accessible from the web
+ require("jack/static").Static(null, {urls:[""],root:"public"}),
+ // the typical reloader scenario
+ (!options || options.reload) ? require("jack/reloader").Reloader(File.join(File.cwd(), "jackconfig"), "app") :
+ exports.app
+ ]);
+};
+
+// we start the REPL (the interactive JS console) because it is really helpful
+new (require("worker").SharedWorker)("narwhal/repl");
+
18 lib/access.js
@@ -0,0 +1,18 @@
+/**
+ * Defines the capabilities of different users
+ */
+var pageFacets = require("facet/page"),
+ pageChangeFacets = require("facet/page-change"),
+ admins = require("commonjs-utils/settings").admins,
+ Register = require("pintura/security").Register,
+ security = require("pintura/pintura").config.security;
+
+security.getAllowedFacets = function(user, request){
+ if(user){
+ if(admins.indexOf(user.name)>-1){
+ return [pageFacets.AdminFacet, pageChangeFacets.AdminFacet];
+ }
+ return [pageFacets.UserFacet, pageChangeFacets.PublicFacet];
+ }
+ return [pageFacets.PublicFacet, pageChangeFacets.PublicFacet, Register];
+};
8 lib/app.js
@@ -0,0 +1,8 @@
+/**
+ * This is an example Wiki web application written on Pintura
+ */
+
+// registers the HTML representation handler that generates HTML from wiki content
+require("media/wiki-html");
+// Defines the capabilities of the users
+require("access");
92 lib/edit.js
@@ -0,0 +1,92 @@
+require("json");
+var pageName = location.search.match(/page=([^&]+)/);
+pageName = pageName && pageName[1];
+document.title = "Editing " + pageName;
+var request = require("commonjs-utils/jsgi-client").request;
+document.getElementById("main-header").innerHTML = escapeHTML("Editing " + pageName);
+request({
+ uri: "Page/" + pageName,
+ headers: {
+ "accept": "application/javascript, application/json"
+ }
+}).then(function(response){
+ if(!response.headers.username){
+ login();
+ }
+ var page = eval("(" + response.body.join("") + ")");
+ if(typeof page !== "object"){
+ page = {
+ id: pageName,
+ content: ""
+ };
+ }
+ var contentArea = document.getElementById("content-area");
+ contentArea.value = page.content;
+
+ document.getElementById("save-button").onclick = function(){
+ page.content = contentArea.value;
+ request({
+ uri: "/Page/" + pageName,
+ method: "PUT",
+ body: JSON.stringify(page),
+ headers: {
+ "accept": "application/javascript, application/json",
+ "content-type": "application/json"
+ }
+ }).then(function(){
+ // redirect to the page once it is saved
+ location = "/Page/" + pageName;
+ }, errorHandler);
+ };
+
+}, errorHandler);
+
+function login(){
+ document.getElementById("login-form").style.display="block";
+ document.getElementById("sign-in").onclick = function(){
+ userRpc("authenticate").then(function(){
+ document.getElementById("login-form").style.display="none";
+ alert("Logged in");
+ }, errorHandler);
+ };
+ document.getElementById("register").onclick = function(){
+ userRpc("createUser").then(function(){
+ alert("Registered");
+ }, errorHandler);
+ };
+}
+
+function userRpc(method, params){
+ return request({
+ uri: "Class/User",
+ method: "POST",
+ body: JSON.stringify({
+ id:"call-id",
+ method: method,
+ params: [
+ document.getElementById("user").value,
+ document.getElementById("password").value
+ ]
+ }),
+ headers: {
+ "accept": "application/javascript, application/json",
+ "content-type": "application/json"
+ }
+ }).then(function(response){
+ response = eval('(' + response.body.join("") + ')');
+ if(response.error){
+ throw response.error;
+ }
+ return response.result;
+ });
+
+}
+
+function errorHandler(error){
+ alert(error);
+}
+
+
+function escapeHTML(html){
+ return html.replace(/&/g, "&")
+ .replace(/</g, "&lt;");
26 lib/facet/page-change.js
@@ -0,0 +1,26 @@
+/**
+ * These are the page facets for the Wiki example application (in progress)
+ */
+
+var PageChange = require("model/page-change").PageChange,
+ Permissive = require("facet").Permissive,
+ Restrictive = require("facet").Restrictive;
+
+
+// These are the different facets that are available for accessing this data
+// This facet uses the Restrictive constructor, so any modifying action must be explicilty
+// be enabled (by defining a handler)
+exports.PublicFacet = Restrictive(PageChange, {
+ prototype: {
+ },
+ quality:0.5
+
+});
+// note that general users can't change page history
+
+// This facet is for administrators and grants full access to data
+exports.AdminFacet = Permissive(PageChange, {
+ properties: {
+ },
+ quality: 1
+});
36 lib/facet/page.js
@@ -0,0 +1,36 @@
+/**
+ * These are the page facets for the Wiki example application (in progress)
+ */
+
+var Page = require("model/page").Page,
+ Permissive = require("facet").Permissive,
+ Restrictive = require("facet").Restrictive;
+
+
+// These are the different facets that are available for accessing this data
+// This facet uses the Restrictive constructor, so any modifying action must be explicilty
+// be enabled (by defining a handler)
+exports.PublicFacet = Restrictive(Page, {
+ query: function(query, options){
+ query = "?status=published" + (query.match(/^\?\w/) ? "&" : "") + query.substring(1);
+ return Page.query(query, options);
+ },
+ prototype: {
+ },
+ quality:0.5
+
+});
+
+// This facet has for authenticated users and grants read and write capabilities
+exports.UserFacet = Permissive(Page, {
+ properties: {
+ },
+ quality: 1
+});
+
+// This facet is for administrators and grants full access to data
+exports.AdminFacet = Permissive(Page, {
+ properties: {
+ },
+ quality: 1
+});
30 lib/index.js
@@ -0,0 +1,30 @@
+var pinturaApp;
+require.reloadable(function(){
+ pinturaApp = require("pintura/pintura").app;
+ require("app");
+});
+
+require("jsgi-node").start(
+ require("pintura/jsgi/cascade").Cascade([
+ // cascade from static to pintura REST handling
+ // the main place for static files accessible from the web
+ require("pintura/jsgi/static").Static({urls:[""],roots:["public"]}),
+ require("pintura/jsgi/static").Static({urls:["explorer"],roots:[require("web-modules").getCachePath("persevere/")]}),
+ require("pintura/jsgi/static").Static({urls:["js/persevere"],roots:[require("web-modules").getCachePath("persevere/")]}),
+ // this will provide access to the server side JS libraries from the client
+ require("transporter/jsgi/transporter").Transporter({
+ loader: require("web-modules").forEngine("browser").useLocal().getModuleSource}),
+ // make the root url redirect to /Page/Root
+ require("pintura/jsgi/redirect-root").RedirectRoot(
+ // main pintura app
+ function(request){
+ return pinturaApp(request);
+ }
+ )
+]));
+
+// having a REPL is really helpful
+require("repl").start();
+
+// this is just to ensure the static analysis preloads the explorer package
+false&&require("persevere/explorer/explorer.js");
9 lib/jsgi/redirect-root.js
@@ -0,0 +1,9 @@
+var redirector = require("jack/redirect").Redirect("/Page/Example");
+exports.RedirectRoot = function(app){
+ return function(request){
+ if(request.pathInfo == "/"){
+ return redirector(request);
+ }
+ return app(request);
+ };
+};
50 lib/media/wiki-html.js
@@ -0,0 +1,50 @@
+/**
+* Media handler for generating HTML from Wiki markup-based pages
+*/
+
+var Media = require("media").Media,
+ escapeHTML = require("narwhal/html").escape,
+ wikiToHtml = require("wiky/wiky").toHtml;
+
+
+Media({
+ mediaType:"text/html",
+ getQuality: function(object){
+ return 1;
+ },
+ serialize: function(object, request, response){
+ var pageName = escapeHTML(request.pathInfo.substring(1));
+ var action;
+ if(response.status === 404){
+ action = "create";
+
+ object = "This page does not exist yet";
+ }
+ else if(response.status === 200){
+ action = "edit";
+ }
+ return {
+ forEach:function(write){
+ write('<html><title>' + pageName + '</title>\n');
+ write('<style type="text/css">@import "/css/common.css";</style>\n');
+ write('<body><div id="headerContainer"><span class="pageName">' + pageName + '<span></div>\n');
+ write('<div id="content">\n');
+ if(typeof object === "object"){
+ write('' + wikiToHtml(object.content));
+ }
+ else{
+ write("<p>" + object + "</p>\n");
+ }
+ if(action){
+ write('<p><a href="/edit.html?page=' + pageName + '">' + action + ' this page</a></p>\n');
+ }
+ write('</div></body></html>\n');
+ }
+ };
+ }
+});
+
+var rules = require("wiky/wiky").rules,
+ store = require("wiky/wiky").store;
+// add a rule for [[target page]] style links
+rules.wikiinlines.push({ rex:/\[\[([^\]]*)\]\]/g, tmplt:function($0,$1,$2){return store("<a href=\""+$1+"\">"+$1+"</a>");}});
39 lib/model/page-change.js
@@ -0,0 +1,39 @@
+/**
+ * This class is used for tracking all the changes of a page
+ */
+
+var Model = require("model").Model,
+ DefaultStore = require("perstore/stores").DefaultStore,
+ auth = require("pintura/jsgi/auth");
+
+// This class contains
+var pageChangeStore = DefaultStore("PageChange");
+/* We can switch to the SQL based back-end with:
+pageChangeStore = SQLStore({
+ table: "PageChange",
+ idColumn: "id"
+ indexedProperties:{
+ id: true,
+ pageId: true
+ }
+});
+*/
+
+// now we create a class, all central model logic is defined here
+exports.PageChange = Model("PageChange", pageChangeStore, {
+ properties: {
+ content: String,
+ pageId: {
+ type: "integer",
+ description:"This is the id for the current page from the Page model"
+ }
+ },
+ links:[
+ {
+ rel: "current",
+ href: "../Page/{pageId}"
+ }
+ ]
+});
+
+// The facets for accessing the page class are defined in facet/page
13 lib/model/page-sql.js
@@ -0,0 +1,13 @@
+/**
+ * This provides the definition of the SQL store if used
+ */
+
+var SQLStore = require("store/sql").SQLStore;
+
+exports.pageStore = SQLStore({
+ table: "Page",
+ starterStatements: [
+ "CREATE TABLE Page (id INT NOT NULL AUTO_INCREMENT, title VARCHAR(100), status VARCHAR(10), content VARCHAR(100000), PRIMARY KEY(id))",
+ ],
+ idColumn: "id"
+});
102 lib/model/page.js
@@ -0,0 +1,102 @@
+/**
+ * This is a page model for the Wiki example application (in progress)
+ */
+
+var Model = require("model").Model,
+ DefaultStore = require("perstore/stores").DefaultStore,
+ auth = require("pintura/jsgi/auth"),
+ Notifying = require("pintura/store/notifying").Notifying,
+ PageChange = require("./page-change").PageChange;
+
+// Pintura consists of three primary layers: the store, the class (which acts as the
+// model), and the facet. Here we define the store and create the class for the store
+// First we create the store for interacting directly with the storage endpoint
+var pageStore = DefaultStore("Page");
+/* We can switch to the SQL based back-end with:
+pageStore = require("./page-sql").pageStore;
+*/
+
+
+/* To add full-text indexing (only supported in Rhino)
+pageStore = require("store/full-text").FullText(pageStore, "Page");
+*/
+
+// to add events
+pageStore = Notifying(pageStore, "Page");
+
+// now we create a class, all central model logic is defined here
+exports.Page = Model("Page", pageStore, {
+/* We can create handlers for any of the actions, they will go directly to the store otherwise
+ query: function(query, options){
+ var sqlCondition = this.getWhereClause(query, options);
+
+ if(sqlCondition){
+ return pageStore.executeQuery(
+ "SELECT id, title FROM Page WHERE " + sqlCondition, options);
+ }
+ },
+*/
+/*
+ "delete": function(id){
+ // any logic that we want on deletes
+ // now call the store to actually do the delete
+ pageStore["delete"](id);
+ },
+*/
+
+ construct: function(page, directives){
+ // set initial properties on object instantiation
+ if(auth.currentUser){
+ page.createdBy = auth.currentUser.username;
+ }
+ page.status = "published";
+ return page.save(directives);
+ },
+
+ put: function(page, options){ // handle puts to add to history and define attribution
+ if(auth.currentUser){
+ // set the current user name as the lastModifiedBy property
+ page.lastModifiedBy = auth.currentUser.username;
+ }
+ page.status = "published";
+ // do the default action of saving to the store
+ var pageId = pageStore.put(page, options) || page.id;
+ // create a new change entry in the history log
+ new PageChange({
+ content: page.content,
+ pageId: pageId
+ });
+ return pageId;
+ },
+
+ properties: { // schema definitions for property types (these are optional)
+ status: String,
+ content: String
+ },
+
+ prototype: { // define the methods available on the model object instances
+ // these are used by atom
+ getTitle: function(item){
+ return item.name;
+ },
+ getSummary: function(item){
+ return item.description;
+ },
+ getUpdated: function(item){
+ return item.uploaded;
+ }
+
+ },
+ links: [ // define the link relations with other objects
+ {
+ rel: "history", // link to the list of changes for a page
+ href: "../PageChange/?pageId={id}"
+ }
+ ]
+
+ // this is flag to indicate that if Perstore should check to see an object exists when a PUT occurs, and call construct if it doesn't. The default is false
+ // noConstructPut: false,
+});
+
+// The facets for accessing the page class are defined in facet/page
+
17 lib/util/narwhal-compat.js
@@ -0,0 +1,17 @@
+var defaultLoader = require.loader;
+var packages = ["perstore", "pintura", "commonjs-utils", "wiky"];
+try{
+ require("perstore/model");
+}catch(e){
+ var defaultResolvePkg = require.loader.resolvePkg;
+ require.loader.resolvePkg = function(id, baseId, pkg, basePkg){
+ var packageName;
+ for(var i = 0; i < packages.length; i++){
+ packageName = packages[i];
+ if(id.substring(0, packageName.length) == packageName){
+ return defaultResolvePkg(id.substring(packageName.length + 1), baseId, pkg, basePkg);
+ }
+ }
+ return defaultResolvePkg(id, baseId, pkg, basePkg);
+ };
+}
16 local.json
@@ -0,0 +1,16 @@
+{
+ "database": {
+ "connection":"jdbc:mysql://localhost/wiki?user=root&password=&useUnicode=true&characterEncoding=utf-8&autoReconnect=true",
+ "type": "mysql",
+ "host": "localhost",
+ "port": 27017,
+ "name": "wiki"
+ },
+ "mail": {
+ "host":"mail.site.com",
+ "defaultFrom": "app@site.com"
+ },
+ "bypassSecurity": true,
+ "admins":["admin"],
+ "dataFolder": "data"
+}
19 package.json
@@ -0,0 +1,19 @@
+{
+ "name": "",
+ "author": "",
+ "dependencies": ["persevere"],
+ "contributors": [],
+ "mappings":{
+ "perstore/": "jar:http://github.com/kriszyp/perstore/zipball/master!/lib/",
+ "commonjs-utils/": "jar:http://github.com/kriszyp/commonjs-utils/zipball/master!/lib/",
+ "pintura/": "jar:http://github.com/kriszyp/pintura/zipball/master!/lib/",
+ "wiky/": "jar:http://github.com/kriskowal/wiky/zipball/master!/lib/",
+ "narwhal/": "jar:http://github.com/kriszyp/narwhal/zipball/master!/lib/",
+ "transporter/": "jar:http://github.com/kriszyp/transporter/zipball/master!/lib/",
+ "^jsgi-node$": "jar:http://github.com/kriszyp/jsgi-node/zipball/master!/lib/jsgi-node",
+ "^model$": "jar:http://github.com/kriszyp/perstore/zipball/master!/lib/model",
+ "^facet$": "jar:http://github.com/kriszyp/perstore/zipball/master!/lib/facet",
+ "^media$": "jar:http://github.com/kriszyp/pintura/zipball/master!/lib/media",
+ "^json$": "jar:http://github.com/kriszyp/narwhal/zipball/master!/engines/default/lib/json"
+ }
+}
47 public/admin.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>dojox.grid.DataGrid with Persevere</title>
+
+ <style type="text/css">
+ @import "../js/dojo/resources/dojo.css";
+ @import "../js/dojox/grid/resources/Grid.css";
+ @import "../js/dojox/grid/resources/tundraGrid.css";
+ @import "../js/dijit/themes/tundra/tundra.css";
+ body {
+ padding:2em !important;
+ font-size: 0.9em;
+ font-family: Geneva, Arial, Helvetica, sans-serif;
+ }
+ .login-link {
+ float: right;
+ }
+ </style>
+ <script djConfig="isDebug:true, parseOnLoad: true" src="../js/dojo/dojo.js" type="text/javascript"></script>
+ <script src="../js/admin.js" type="text/javascript"></script>
+</head>
+<body class="tundra">
+
+ <div dojoType="persevere.LoginLink" mustLogin="true" jsid="loginLink" class="login-link"></div>
+ <h1>Example Wiki Adminstration</h1>
+
+ <table rowSelector="20px" rowsPerPage="20" query="''" height="200px" store="pageStore" id="grid" jsid="grid" dojoType="dojox.grid.DataGrid">
+ <thead>
+ <tr>
+ <th field="id">Page</th>
+ <th editable="true" field="status">Status</th>
+ <th editable="true" field="createdBy">Created By</th>
+ <th editable="true" field="lastModifiedBy">Last Modified By</th>
+ </tr>
+ </thead>
+ </table>
+ <div id="controls">
+ <button onclick="pageStore.save({onError:function(e){console.log('the error', e)}})">Save</button>
+ <button onclick="pageStore.revert()">Revert</button>
+ <button onclick="addItem()">Add</button>
+ <button onclick="remove()">Remove</button>&nbsp;&nbsp;&nbsp;
+ </div>
+
+
+</body>
+</html>
128 public/css/common.css
@@ -0,0 +1,128 @@
+/* ---------- reset ----------*/
+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,
+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 {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ list-style: none;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+
+html, body {
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ font-family: "Trebuchet MS", Verdana, Helvetica, sans-serif;
+ color: #111;
+ background-color:#e8eeef;
+}
+p{
+padding-top:5px;
+padding-bottom:5px;
+}
+
+h1 {
+ font-weight:bold;
+ font-size:1.2em;
+ margin-bottom:1em;
+}
+
+/* ---------- header ----------*/
+#headerContainer {
+ background-color: #ddd;
+ width: 100%;
+ height: 46px;
+ border-top: 5px solid #ccc;
+ border-bottom: 5px solid #ccc;
+ font-size: 30px;
+ padding:10px;
+}
+#headerContainer span {
+ margin-left: 40px;
+}
+
+#headerContainer h1 {
+ font-size:20px;
+ display: block;
+ float: right;
+}
+
+/* ---------- navigation ----------*/
+#mainNav {
+ position: absolute;
+ right: 0px;
+ font-size: 14px;
+ margin: 35px 126px 0 0;
+ background-color: #ddd;
+}
+
+#headerContainer #subNav {
+ margin-top:76px;
+}
+
+.pageName {
+ color: #0093C8;
+}
+
+
+
+/* ---------- content ----------*/
+
+#content {
+ background: #eef2f3 url("../images/bodyBg.png") repeat-x top left;
+ width: 640px;
+ min-height: 250px;
+ height:auto !important;
+ height: 250px;
+ padding: 40px;
+ margin: 25px;
+ -moz-border-radius: 15px;
+ -webkit-border-radius: 15px;
+}
+
+#content h2 {
+ background: #E6E6FF;
+ height: 16px;
+ color: #7D3333;
+ font-size: 16px;
+ font-weight: normal;
+ padding: 8px;
+ font-family: "Myriad Pro", "Trebuchet MS", Verdana, Helvetica, sans-serif;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+ border: 1px solid #E1E1E1;
+
+ margin: 10px;
+ width:300px;
+
+}
+
+#content h3 {
+ height: 16px;
+ margin: 15px;
+ color: #515152;
+ font-size: 14px;
+ font-weight: bold;
+ font-family: Arial, Verdana, Helvetica, sans-serif;
+}
+
+
+#status {
+ position:absolute;
+ top:0px;
+ left:0px;
+ z-index:10;
+ background:url('spinner.gif') white no-repeat;
+ border:1px solid black;
+ padding:5px;
+ padding-left:25px;
+ font-family:arial;
+ font-size: 80%;
+}
+
21 public/css/edit.css
@@ -0,0 +1,21 @@
+#login-form, #upload-form {
+ display: none;
+ position: absolute;
+ top: 200px;
+ left: 200px;
+ border: solid 2px;
+ background-color: #bbb;
+ -moz-border-radius: 7px;
+ -webkit-border-radius: 7px;
+ padding: 20px;
+}
+
+#user-name {
+ float: right;
+}
+
+#content-area {
+ display: block;
+ width: 640px;
+ height: 480px;
+}
37 public/edit.html
@@ -0,0 +1,37 @@
+<html>
+ <head>
+ <title>Editing content</title>
+ <style type="text/css">
+ @import "css/edit.css";
+ @import "css/common.css";
+ </style>
+ </head>
+ <body>
+ <div id="headerContainer">
+ <div id="main-header">Editing page</div>
+ </div>
+ <div id="content">
+ <div id="user-name"></div>
+ <textarea id="content-area"></textarea>
+ <button id="save-button">Save</button>
+ </div>
+
+
+ <div id="login-form">
+ <label>Name:</label><input id="user" type="text" /><br />
+ <label>Password:</label><input id="password" type="password" /><br />
+ <button id="sign-in">Sign in</button>
+ <button id="register">Register</button>
+ </div>
+ <form id="upload-form" method="POST" enctype="multipart/form-data" target="upload-target" action="/Page/Example">
+ <label>Image to upload:</label><input type="file" name="image"/><br />
+ <button>Upload</input>
+ <iframe name="upload-target" style="display:none"></iframe>
+ </form>
+ <script>
+ require = {baseUrl: "lib/"};
+ </script>
+ <script src="http://requirejs.org/docs/release/0.9.0/minified/require.js"></script>
+ <script src="lib/edit.js"></script>
+ </body>
+</html>
BIN  public/images/bodyBg.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 public/js/admin.js
@@ -0,0 +1,32 @@
+var path = location.pathname.match(/(.*\/)[^\/]*$/)[1];
+if(location.toString().match(/file:/)){
+ alert("You can't run this demo from the file system, you must run it with Persevere");
+}
+dojo.require("dojo.cookie");
+dojo.require("dojox.data.JsonRestStore");
+dojo.require("dojox.grid.DataGrid");
+dojo.require("persevere.LoginLink");
+
+if(path.lastIndexOf("example/") > -1){
+ path = path.substring(0,path.lastIndexOf("example/"));
+}
+
+pageStore = new dojox.data.JsonRestStore({target: "Page/"});
+if(!pageStore){
+ console.log("Customer table doesn't exist, creating it now");
+ dojo.xhr("POST",{url:"/Class/",sync: true, putData:'{"id":"Customer","extends":{"$ref":"Object"}}'},true);
+ pageStore = new dojox.data.PersevereStore({target:"/Customer/"}); // and get the Customer store
+ // create a new item for starting data
+ pageStore.newItem({firstName:"John", lastName:"Doe"});
+ pageStore.save();
+}
+
+addItem = function() {
+ pageStore.newItem({firstName: "firstName", lastName: "lastName",created:dojo.date.stamp.toISOString(new Date,{zulu:true})});
+}
+remove = function() {
+ var items = grid.selection.getSelected();
+ for (var i = 0; i < items.length; i++){
+ pageStore.deleteItem(items[i]);
+ }
+}
58 start-node.js
@@ -0,0 +1,58 @@
+/**
+ * The starting point for Pintura running in Node.
+ */
+
+// first we add all the necessary paths to require.paths
+var packagesRoot = "../../../";
+var packagePaths = [""] // start with the current directory
+ .concat([ // now add alll the packages
+ "packages/pintura/",
+ "packages/pintura/engines/node/",
+ "packages/pintura/engines/default/",
+ "packages/perstore/",
+ "packages/perstore/engines/node/",
+ "packages/perstore/engines/default/",
+ "packages/commonjs-utils/",
+ "packages/jack/",
+ "packages/jsgi-node/",
+ "packages/wiky/",
+ "engines/default/",
+ ""
+ ].map(function(path){ // for each package, start in the right directory
+ return packagesRoot + path;
+ }));
+
+require.paths.unshift.apply(require.paths, packagePaths.slice(0, -2).map(addLib));
+require.paths.push.apply(require.paths, packagePaths.slice(-2).map(addLib));
+function addLib(path){
+ return path + "lib";
+}
+require("node-commonjs");
+require.reloadable(function(){
+ var pintura = require("pintura");
+ require("app");
+});
+
+require("jsgi-node").start(
+ require("jsgi/cascade").Cascade([
+ // cascade from static to pintura REST handling
+ // the main place for static files accessible from the web
+ require("jsgi/static").Static({urls:[""],roots:["public"]}),
+ // this will provide access to the server side JS libraries from the client
+ require("jsgi/transporter").Transporter({paths: [packagesRoot + "engines/browser/lib"].concat(require.paths.map(function(path){
+ return path.replace(/[\\\/]engines[\\\/](\w*)/,function(t, engine){
+ return "/engines/" + (engine === "default" ? "default" : "browser");
+ })
+ }))}),
+ // make the root url redirect to /Page/Root
+ require("jsgi/redirect-root").RedirectRoot(
+ // main pintura app
+ function(request){
+ return pintura.app(request);
+ }
+ )
+]));
+
+// having a REPL is really helpful
+require("repl").start();
+
Please sign in to comment.
Something went wrong with that request. Please try again.