Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial revision

  • Loading branch information...
commit 1f3bd20b2eed94c49653be8910d85cd32520c767 0 parents
@paulcarey authored
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Paul Carey
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 README.textile
@@ -0,0 +1,52 @@
+h3. Overview
+
+Fuschia is a graphical document browser for CouchDB. It's built on prefuse and Flex.
+
+h3. Using
+
+Clone this repo, set @DB_NAME@ in @upload_binaries.sh@ to your target database and run @./upload_binaries.sh@. This will create a design document named fuschia containing views that, for a given document, retrieve all documents that it points to, and all documents that point to it.
+
+Now direct your browser at http://localhost:5984/mydb/_design/fuschia/Fuschia.html, enter a full database uri (e.g. http://localhost:5984/mydb), seed document id and click load. You should be able to browse through your database.
+
+Fushica is at its best when it colors document nodes and displays document summaries. To have this happen, create a document at @mydb/fuschia@ based on the following example. You can upload it easily with curl, for example, @curl -T fuschia.json localhost:5984/mydb/fuschia@.
+
+<pre>
+<code>
+// classProperty is used to distinguish doc types. So for RelaxDB this would be 'class', for CouchRest::Model, 'couchrest-type'.
+// fillColor represents the document node color. The first two characters specify an alpha value.
+// summaryProperty should specify the most descriptive property of the document.
+{
+ "_id" : "fuschia",
+ "classProperty" : "class",
+ "User" : {
+ "fillColor" : "0xff3355cc",
+ "summaryProperty" : "name"
+ },
+ "Post" : {
+ "fillColor" : "0xffcc5533",
+ "summaryProperty" : "contents"
+ },
+ "Comment" : {
+ "fillColor" : "0xff33cc55",
+ "summaryProperty" : "contents"
+ }
+}
+</code>
+</pre>
+
+
+Note: If your database doesn't use standard UUIDs, modify @isDocId@ in @src/fuschia_queries.js@ as necessary and overwrite @bin/fuschia_queries.json@ with your own version.
+
+h3. Hacking
+
+The "flare tutorial":http://flare.prefuse.org/tutorial is the best place to get started with flare. If you're new to CouchDB, the "wiki":http://wiki.apache.org/couchdb/ is a great place to begin.
+
+Instructions on developing with "flare in textmate":http://arrifana.org/blog/2008/03/flare-flex-sdk-and-mac-osx/.
+
+h3. Known Issues
+
+ * Edges don't display bi-directional arrows
+
+h3. Example
+
+The example directory contains a script that feeds some sample data into CouchDB using RelaxDB. However, following the instructions in the 'Using' section above, you should be able to explore any CouchDB database that contains inter-document references.
292 bin/AC_OETags.js
@@ -0,0 +1,292 @@
+// Flash Player Version Detection - Rev 1.6
+// Detect Client Browser type
+// Copyright(c) 2005-2006 Adobe Macromedia Software, LLC. All rights reserved.
+var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
+var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
+var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
+
+function ControlVersion()
+{
+ var version;
+ var axo;
+ var e;
+
+ // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry
+
+ try {
+ // version will be set for 7.X or greater players
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 6.X players only
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+
+ // installed player is some revision of 6.0
+ // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
+ // so we have to be careful.
+
+ // default to the first public version
+ version = "WIN 6,0,21,0";
+
+ // throws if AllowScripAccess does not exist (introduced in 6.0r47)
+ axo.AllowScriptAccess = "always";
+
+ // safe to call for 6.0r47 or greater
+ version = axo.GetVariable("$version");
+
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 4.X or 5.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 3.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = "WIN 3,0,18,0";
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 2.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ version = "WIN 2,0,0,11";
+ } catch (e) {
+ version = -1;
+ }
+ }
+
+ return version;
+}
+
+// JavaScript helper required to detect Flash Player PlugIn version information
+function GetSwfVer(){
+ // NS/Opera version >= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ var versionRevision = descArray[3];
+ if (versionRevision == "") {
+ versionRevision = descArray[4];
+ }
+ if (versionRevision[0] == "d") {
+ versionRevision = versionRevision.substring(1);
+ } else if (versionRevision[0] == "r") {
+ versionRevision = versionRevision.substring(1);
+ if (versionRevision.indexOf("d") > 0) {
+ versionRevision = versionRevision.substring(0, versionRevision.indexOf("d"));
+ }
+ } else if (versionRevision[0] == "b") {
+ versionRevision = versionRevision.substring(1);
+ }
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ flashVer = ControlVersion();
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ versionStr = GetSwfVer();
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ // Given "WIN 2,0,0,11"
+ tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"]
+ tempString = tempArray[1]; // "2,0,0,11"
+ versionArray = tempString.split(","); // ['2', '0', '0', '11']
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > parseFloat(reqMajorVer)) {
+ return true;
+ } else if (versionMajor == parseFloat(reqMajorVer)) {
+ if (versionMinor > parseFloat(reqMinorVer))
+ return true;
+ else if (versionMinor == parseFloat(reqMinorVer)) {
+ if (versionRevision >= parseFloat(reqRevision))
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+function AC_AddExtension(src, ext)
+{
+ var qIndex = src.indexOf('?');
+ if ( qIndex != -1)
+ {
+ // Add the extention (if needed) before the query params
+ var path = src.substring(0, qIndex);
+ if (path.length >= ext.length && path.lastIndexOf(ext) == (path.length - ext.length))
+ return src;
+ else
+ return src.replace(/\?/, ext+'?');
+ }
+ else
+ {
+ // Add the extension (if needed) to the end of the URL
+ if (src.length >= ext.length && src.lastIndexOf(ext) == (src.length - ext.length))
+ return src; // Already have extension
+ else
+ return src + ext;
+ }
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+ if (isIE && isWin && !isOpera)
+ {
+ str += '<object ';
+ for (var i in objAttrs)
+ str += i + '="' + objAttrs[i] + '" ';
+ str += '>';
+ for (var i in params)
+ str += '<param name="' + i + '" value="' + params[i] + '" /> ';
+ str += '</object>';
+ } else {
+ str += '<embed ';
+ for (var i in embedAttrs)
+ str += i + '="' + embedAttrs[i] + '" ';
+ str += '> </embed>';
+ }
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ args[i+1] = AC_AddExtension(args[i+1], ext);
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "id":
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
+
121 bin/Fuschia.html
@@ -0,0 +1,121 @@
+<!-- saved from url=(0014)about:internet -->
+<html lang="en">
+
+<!--
+Smart developers always View Source.
+
+This application was built using Adobe Flex, an open source framework
+for building rich Internet applications that get delivered via the
+Flash Player or to desktops via Adobe AIR.
+
+Learn more about Flex at http://flex.org
+// -->
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+<!-- BEGIN Browser History required section -->
+<link rel="stylesheet" type="text/css" href="history/history.css" />
+<!-- END Browser History required section -->
+
+<title></title>
+<script src="AC_OETags.js" language="javascript"></script>
+
+<!-- BEGIN Browser History required section -->
+<script src="history/history.js" language="javascript"></script>
+<!-- END Browser History required section -->
+
+<style>
+body { margin: 0px; overflow:hidden }
+</style>
+<script language="JavaScript" type="text/javascript">
+<!--
+// -----------------------------------------------------------------------------
+// Globals
+// Major version of Flash required
+var requiredMajorVersion = 9;
+// Minor version of Flash required
+var requiredMinorVersion = 0;
+// Minor version of Flash required
+var requiredRevision = 124;
+// -----------------------------------------------------------------------------
+// -->
+</script>
+</head>
+
+<body scroll="no">
+<script language="JavaScript" type="text/javascript">
+<!--
+// Version check for the Flash Player that has the ability to start Player Product Install (6.0r65)
+var hasProductInstall = DetectFlashVer(6, 0, 65);
+
+// Version check based upon the values defined in globals
+var hasRequestedVersion = DetectFlashVer(requiredMajorVersion, requiredMinorVersion, requiredRevision);
+
+if ( hasProductInstall && !hasRequestedVersion ) {
+ // DO NOT MODIFY THE FOLLOWING FOUR LINES
+ // Location visited after installation is complete if installation is required
+ var MMPlayerType = (isIE == true) ? "ActiveX" : "PlugIn";
+ var MMredirectURL = window.location;
+ document.title = document.title.slice(0, 47) + " - Flash Player Installation";
+ var MMdoctitle = document.title;
+
+ AC_FL_RunContent(
+ "src", "playerProductInstall",
+ "FlashVars", "MMredirectURL="+MMredirectURL+'&MMplayerType='+MMPlayerType+'&MMdoctitle='+MMdoctitle+"",
+ "width", "1000",
+ "height", "700",
+ "align", "middle",
+ "id", "Fuschia",
+ "quality", "high",
+ "bgcolor", "#ffffff",
+ "name", "Fuschia",
+ "allowScriptAccess","sameDomain",
+ "type", "application/x-shockwave-flash",
+ "pluginspage", "http://www.adobe.com/go/getflashplayer"
+ );
+} else if (hasRequestedVersion) {
+ // if we've detected an acceptable version
+ // embed the Flash Content SWF when all tests are passed
+ AC_FL_RunContent(
+ "src", "Fuschia",
+ "width", "1000",
+ "height", "700",
+ "align", "middle",
+ "id", "Fuschia",
+ "quality", "high",
+ "bgcolor", "#ffffff",
+ "name", "Fuschia",
+ "allowScriptAccess","sameDomain",
+ "type", "application/x-shockwave-flash",
+ "pluginspage", "http://www.adobe.com/go/getflashplayer"
+ );
+ } else { // flash is too old or we can't detect the plugin
+ var alternateContent = 'Alternate HTML content should be placed here. '
+ + 'This content requires the Adobe Flash Player. '
+ + '<a href=http://www.adobe.com/go/getflash/>Get Flash</a>';
+ document.write(alternateContent); // insert non-flash content
+ }
+// -->
+</script>
+<noscript>
+ <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+ id="Fuschia" width="1000" height="700"
+ codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
+ <param name="movie" value="Fuschia.swf" />
+ <param name="quality" value="high" />
+ <param name="bgcolor" value="#ffffff" />
+ <param name="allowScriptAccess" value="sameDomain" />
+ <embed src="Fuschia.swf" quality="high" bgcolor="#ffffff"
+ width="1000" height="700" name="Fuschia" align="middle"
+ play="true"
+ loop="false"
+ quality="high"
+ allowScriptAccess="sameDomain"
+ type="application/x-shockwave-flash"
+ pluginspage="http://www.adobe.com/go/getflashplayer">
+ </embed>
+ </object>
+</noscript>
+</body>
+</html>
BIN  bin/Fuschia.swf
Binary file not shown
11 bin/fuschia_queries.json
@@ -0,0 +1,11 @@
+{
+ "_id":"_design/fuschia",
+ "views":{
+ "to_doc":{
+ "map":"function (doc) {\n var isDocId = function (str) {\n var res = /[0-9a-f-]+/(str);\n return res && str.length >= 32 && str === res[0];\n };\n \n for(var n in doc) {\n var att = doc[n];\n if (n !== \"_id\" && isDocId(att)) {\n emit(att, null);\n }\n }\n}\n\n"
+ },
+ "from_doc":{
+ "map":"function (doc) {\n var isDocId = function (str) {\n var res = /[0-9a-f-]+/(str);\n return res && str.length >= 32 && str === res[0];\n };\n \n for(var n in doc) {\n var att = doc[n];\n if (n !== \"_id\" && isDocId(att)) {\n emit(doc._id, att);\n }\n }\n}\n"
+ }
+ }
+}
6 bin/history/history.css
@@ -0,0 +1,6 @@
+/* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */
+
+#ie_historyFrame { width: 0px; height: 0px; display:none }
+#firefox_anchorDiv { width: 0px; height: 0px; display:none }
+#safari_formDiv { width: 0px; height: 0px; display:none }
+#safari_rememberDiv { width: 0px; height: 0px; display:none }
662 bin/history/history.js
@@ -0,0 +1,662 @@
+BrowserHistoryUtils = {
+ addEvent: function(elm, evType, fn, useCapture) {
+ useCapture = useCapture || false;
+ if (elm.addEventListener) {
+ elm.addEventListener(evType, fn, useCapture);
+ return true;
+ }
+ else if (elm.attachEvent) {
+ var r = elm.attachEvent('on' + evType, fn);
+ return r;
+ }
+ else {
+ elm['on' + evType] = fn;
+ }
+ }
+}
+
+BrowserHistory = (function() {
+ // type of browser
+ var browser = {
+ ie: false,
+ firefox: false,
+ safari: false,
+ opera: false,
+ version: -1
+ };
+
+ // if setDefaultURL has been called, our first clue
+ // that the SWF is ready and listening
+ //var swfReady = false;
+
+ // the URL we'll send to the SWF once it is ready
+ //var pendingURL = '';
+
+ // Default app state URL to use when no fragment ID present
+ var defaultHash = '';
+
+ // Last-known app state URL
+ var currentHref = document.location.href;
+
+ // Initial URL (used only by IE)
+ var initialHref = document.location.href;
+
+ // Initial URL (used only by IE)
+ var initialHash = document.location.hash;
+
+ // History frame source URL prefix (used only by IE)
+ var historyFrameSourcePrefix = 'history/historyFrame.html?';
+
+ // History maintenance (used only by Safari)
+ var currentHistoryLength = -1;
+
+ var historyHash = [];
+
+ var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash);
+
+ var backStack = [];
+ var forwardStack = [];
+
+ var currentObjectId = null;
+
+ //UserAgent detection
+ var useragent = navigator.userAgent.toLowerCase();
+
+ if (useragent.indexOf("opera") != -1) {
+ browser.opera = true;
+ } else if (useragent.indexOf("msie") != -1) {
+ browser.ie = true;
+ browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4));
+ } else if (useragent.indexOf("safari") != -1) {
+ browser.safari = true;
+ browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7));
+ } else if (useragent.indexOf("gecko") != -1) {
+ browser.firefox = true;
+ }
+
+ if (browser.ie == true && browser.version == 7) {
+ window["_ie_firstload"] = false;
+ }
+
+ // Accessor functions for obtaining specific elements of the page.
+ function getHistoryFrame()
+ {
+ return document.getElementById('ie_historyFrame');
+ }
+
+ function getAnchorElement()
+ {
+ return document.getElementById('firefox_anchorDiv');
+ }
+
+ function getFormElement()
+ {
+ return document.getElementById('safari_formDiv');
+ }
+
+ function getRememberElement()
+ {
+ return document.getElementById("safari_remember_field");
+ }
+
+ // Get the Flash player object for performing ExternalInterface callbacks.
+ // Updated for changes to SWFObject2.
+ function getPlayer(id) {
+ if (id && document.getElementById(id)) {
+ var r = document.getElementById(id);
+ if (typeof r.SetVariable != "undefined") {
+ return r;
+ }
+ else {
+ var o = r.getElementsByTagName("object");
+ var e = r.getElementsByTagName("embed");
+ if (o.length > 0 && typeof o[0].SetVariable != "undefined") {
+ return o[0];
+ }
+ else if (e.length > 0 && typeof e[0].SetVariable != "undefined") {
+ return e[0];
+ }
+ }
+ }
+ else {
+ var o = document.getElementsByTagName("object");
+ var e = document.getElementsByTagName("embed");
+ if (e.length > 0 && typeof e[0].SetVariable != "undefined") {
+ return e[0];
+ }
+ else if (o.length > 0 && typeof o[0].SetVariable != "undefined") {
+ return o[0];
+ }
+ else if (o.length > 1 && typeof o[1].SetVariable != "undefined") {
+ return o[1];
+ }
+ }
+ return undefined;
+ }
+
+ function getPlayers() {
+ var players = [];
+ if (players.length == 0) {
+ var tmp = document.getElementsByTagName('object');
+ players = tmp;
+ }
+
+ if (players.length == 0 || players[0].object == null) {
+ var tmp = document.getElementsByTagName('embed');
+ players = tmp;
+ }
+ return players;
+ }
+
+ function getIframeHash() {
+ var doc = getHistoryFrame().contentWindow.document;
+ var hash = String(doc.location.search);
+ if (hash.length == 1 && hash.charAt(0) == "?") {
+ hash = "";
+ }
+ else if (hash.length >= 2 && hash.charAt(0) == "?") {
+ hash = hash.substring(1);
+ }
+ return hash;
+ }
+
+ /* Get the current location hash excluding the '#' symbol. */
+ function getHash() {
+ // It would be nice if we could use document.location.hash here,
+ // but it's faulty sometimes.
+ var idx = document.location.href.indexOf('#');
+ return (idx >= 0) ? document.location.href.substr(idx+1) : '';
+ }
+
+ /* Get the current location hash excluding the '#' symbol. */
+ function setHash(hash) {
+ // It would be nice if we could use document.location.hash here,
+ // but it's faulty sometimes.
+ if (hash == '') hash = '#'
+ document.location.hash = hash;
+ }
+
+ function createState(baseUrl, newUrl, flexAppUrl) {
+ return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null };
+ }
+
+ /* Add a history entry to the browser.
+ * baseUrl: the portion of the location prior to the '#'
+ * newUrl: the entire new URL, including '#' and following fragment
+ * flexAppUrl: the portion of the location following the '#' only
+ */
+ function addHistoryEntry(baseUrl, newUrl, flexAppUrl) {
+
+ //delete all the history entries
+ forwardStack = [];
+
+ if (browser.ie) {
+ //Check to see if we are being asked to do a navigate for the first
+ //history entry, and if so ignore, because it's coming from the creation
+ //of the history iframe
+ if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) {
+ currentHref = initialHref;
+ return;
+ }
+ if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) {
+ newUrl = baseUrl + '#' + defaultHash;
+ flexAppUrl = defaultHash;
+ } else {
+ // for IE, tell the history frame to go somewhere without a '#'
+ // in order to get this entry into the browser history.
+ getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl;
+ }
+ setHash(flexAppUrl);
+ } else {
+
+ //ADR
+ if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) {
+ initialState = createState(baseUrl, newUrl, flexAppUrl);
+ } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) {
+ backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl);
+ }
+
+ if (browser.safari) {
+ // for Safari, submit a form whose action points to the desired URL
+ if (browser.version <= 419.3) {
+ var file = window.location.pathname.toString();
+ file = file.substring(file.lastIndexOf("/")+1);
+ getFormElement().innerHTML = '<form name="historyForm" action="'+file+'#' + flexAppUrl + '" method="GET"></form>';
+ //get the current elements and add them to the form
+ var qs = window.location.search.substring(1);
+ var qs_arr = qs.split("&");
+ for (var i = 0; i < qs_arr.length; i++) {
+ var tmp = qs_arr[i].split("=");
+ var elem = document.createElement("input");
+ elem.type = "hidden";
+ elem.name = tmp[0];
+ elem.value = tmp[1];
+ document.forms.historyForm.appendChild(elem);
+ }
+ document.forms.historyForm.submit();
+ } else {
+ top.location.hash = flexAppUrl;
+ }
+ // We also have to maintain the history by hand for Safari
+ historyHash[history.length] = flexAppUrl;
+ _storeStates();
+ } else {
+ // Otherwise, write an anchor into the page and tell the browser to go there
+ addAnchor(flexAppUrl);
+ setHash(flexAppUrl);
+ }
+ }
+ backStack.push(createState(baseUrl, newUrl, flexAppUrl));
+ }
+
+ function _storeStates() {
+ if (browser.safari) {
+ getRememberElement().value = historyHash.join(",");
+ }
+ }
+
+ function handleBackButton() {
+ //The "current" page is always at the top of the history stack.
+ var current = backStack.pop();
+ if (!current) { return; }
+ var last = backStack[backStack.length - 1];
+ if (!last && backStack.length == 0){
+ last = initialState;
+ }
+ forwardStack.push(current);
+ }
+
+ function handleForwardButton() {
+ //summary: private method. Do not call this directly.
+
+ var last = forwardStack.pop();
+ if (!last) { return; }
+ backStack.push(last);
+ }
+
+ function handleArbitraryUrl() {
+ //delete all the history entries
+ forwardStack = [];
+ }
+
+ /* Called periodically to poll to see if we need to detect navigation that has occurred */
+ function checkForUrlChange() {
+
+ if (browser.ie) {
+ if (currentHref != document.location.href && currentHref + '#' != document.location.href) {
+ //This occurs when the user has navigated to a specific URL
+ //within the app, and didn't use browser back/forward
+ //IE seems to have a bug where it stops updating the URL it
+ //shows the end-user at this point, but programatically it
+ //appears to be correct. Do a full app reload to get around
+ //this issue.
+ if (browser.version < 7) {
+ currentHref = document.location.href;
+ document.location.reload();
+ } else {
+ if (getHash() != getIframeHash()) {
+ // this.iframe.src = this.blankURL + hash;
+ var sourceToSet = historyFrameSourcePrefix + getHash();
+ getHistoryFrame().src = sourceToSet;
+ }
+ }
+ }
+ }
+
+ if (browser.safari) {
+ // For Safari, we have to check to see if history.length changed.
+ if (currentHistoryLength >= 0 && history.length != currentHistoryLength) {
+ //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|"));
+ // If it did change, then we have to look the old state up
+ // in our hand-maintained array since document.location.hash
+ // won't have changed, then call back into BrowserManager.
+ currentHistoryLength = history.length;
+ var flexAppUrl = historyHash[currentHistoryLength];
+ if (flexAppUrl == '') {
+ //flexAppUrl = defaultHash;
+ }
+ //ADR: to fix multiple
+ if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
+ var pl = getPlayers();
+ for (var i = 0; i < pl.length; i++) {
+ pl[i].browserURLChange(flexAppUrl);
+ }
+ } else {
+ getPlayer().browserURLChange(flexAppUrl);
+ }
+ _storeStates();
+ }
+ }
+ if (browser.firefox) {
+ if (currentHref != document.location.href) {
+ var bsl = backStack.length;
+
+ var urlActions = {
+ back: false,
+ forward: false,
+ set: false
+ }
+
+ if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) {
+ urlActions.back = true;
+ // FIXME: could this ever be a forward button?
+ // we can't clear it because we still need to check for forwards. Ugg.
+ // clearInterval(this.locationTimer);
+ handleBackButton();
+ }
+
+ // first check to see if we could have gone forward. We always halt on
+ // a no-hash item.
+ if (forwardStack.length > 0) {
+ if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) {
+ urlActions.forward = true;
+ handleForwardButton();
+ }
+ }
+
+ // ok, that didn't work, try someplace back in the history stack
+ if ((bsl >= 2) && (backStack[bsl - 2])) {
+ if (backStack[bsl - 2].flexAppUrl == getHash()) {
+ urlActions.back = true;
+ handleBackButton();
+ }
+ }
+
+ if (!urlActions.back && !urlActions.forward) {
+ var foundInStacks = {
+ back: -1,
+ forward: -1
+ }
+
+ for (var i = 0; i < backStack.length; i++) {
+ if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
+ arbitraryUrl = true;
+ foundInStacks.back = i;
+ }
+ }
+ for (var i = 0; i < forwardStack.length; i++) {
+ if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) {
+ arbitraryUrl = true;
+ foundInStacks.forward = i;
+ }
+ }
+ handleArbitraryUrl();
+ }
+
+ // Firefox changed; do a callback into BrowserManager to tell it.
+ currentHref = document.location.href;
+ var flexAppUrl = getHash();
+ if (flexAppUrl == '') {
+ //flexAppUrl = defaultHash;
+ }
+ //ADR: to fix multiple
+ if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
+ var pl = getPlayers();
+ for (var i = 0; i < pl.length; i++) {
+ pl[i].browserURLChange(flexAppUrl);
+ }
+ } else {
+ getPlayer().browserURLChange(flexAppUrl);
+ }
+ }
+ }
+ //setTimeout(checkForUrlChange, 50);
+ }
+
+ /* Write an anchor into the page to legitimize it as a URL for Firefox et al. */
+ function addAnchor(flexAppUrl)
+ {
+ if (document.getElementsByName(flexAppUrl).length == 0) {
+ getAnchorElement().innerHTML += "<a name='" + flexAppUrl + "'>" + flexAppUrl + "</a>";
+ }
+ }
+
+ var _initialize = function () {
+ if (browser.ie)
+ {
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0, s; s = scripts[i]; i++) {
+ if (s.src.indexOf("history.js") > -1) {
+ var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html");
+ }
+ }
+ historyFrameSourcePrefix = iframe_location + "?";
+ var src = historyFrameSourcePrefix;
+
+ var iframe = document.createElement("iframe");
+ iframe.id = 'ie_historyFrame';
+ iframe.name = 'ie_historyFrame';
+ //iframe.src = historyFrameSourcePrefix;
+ try {
+ document.body.appendChild(iframe);
+ } catch(e) {
+ setTimeout(function() {
+ document.body.appendChild(iframe);
+ }, 0);
+ }
+ }
+
+ if (browser.safari)
+ {
+ var rememberDiv = document.createElement("div");
+ rememberDiv.id = 'safari_rememberDiv';
+ document.body.appendChild(rememberDiv);
+ rememberDiv.innerHTML = '<input type="text" id="safari_remember_field" style="width: 500px;">';
+
+ var formDiv = document.createElement("div");
+ formDiv.id = 'safari_formDiv';
+ document.body.appendChild(formDiv);
+
+ var reloader_content = document.createElement('div');
+ reloader_content.id = 'safarireloader';
+ var scripts = document.getElementsByTagName('script');
+ for (var i = 0, s; s = scripts[i]; i++) {
+ if (s.src.indexOf("history.js") > -1) {
+ html = (new String(s.src)).replace(".js", ".html");
+ }
+ }
+ reloader_content.innerHTML = '<iframe id="safarireloader-iframe" src="about:blank" frameborder="no" scrolling="no"></iframe>';
+ document.body.appendChild(reloader_content);
+ reloader_content.style.position = 'absolute';
+ reloader_content.style.left = reloader_content.style.top = '-9999px';
+ iframe = reloader_content.getElementsByTagName('iframe')[0];
+
+ if (document.getElementById("safari_remember_field").value != "" ) {
+ historyHash = document.getElementById("safari_remember_field").value.split(",");
+ }
+
+ }
+
+ if (browser.firefox)
+ {
+ var anchorDiv = document.createElement("div");
+ anchorDiv.id = 'firefox_anchorDiv';
+ document.body.appendChild(anchorDiv);
+ }
+
+ //setTimeout(checkForUrlChange, 50);
+ }
+
+ return {
+ historyHash: historyHash,
+ backStack: function() { return backStack; },
+ forwardStack: function() { return forwardStack },
+ getPlayer: getPlayer,
+ initialize: function(src) {
+ _initialize(src);
+ },
+ setURL: function(url) {
+ document.location.href = url;
+ },
+ getURL: function() {
+ return document.location.href;
+ },
+ getTitle: function() {
+ return document.title;
+ },
+ setTitle: function(title) {
+ try {
+ backStack[backStack.length - 1].title = title;
+ } catch(e) { }
+ //if on safari, set the title to be the empty string.
+ if (browser.safari) {
+ if (title == "") {
+ try {
+ var tmp = window.location.href.toString();
+ title = tmp.substring((tmp.lastIndexOf("/")+1), tmp.lastIndexOf("#"));
+ } catch(e) {
+ title = "";
+ }
+ }
+ }
+ document.title = title;
+ },
+ setDefaultURL: function(def)
+ {
+ defaultHash = def;
+ def = getHash();
+ //trailing ? is important else an extra frame gets added to the history
+ //when navigating back to the first page. Alternatively could check
+ //in history frame navigation to compare # and ?.
+ if (browser.ie)
+ {
+ window['_ie_firstload'] = true;
+ var sourceToSet = historyFrameSourcePrefix + def;
+ var func = function() {
+ getHistoryFrame().src = sourceToSet;
+ window.location.replace("#" + def);
+ setInterval(checkForUrlChange, 50);
+ }
+ try {
+ func();
+ } catch(e) {
+ window.setTimeout(function() { func(); }, 0);
+ }
+ }
+
+ if (browser.safari)
+ {
+ currentHistoryLength = history.length;
+ if (historyHash.length == 0) {
+ historyHash[currentHistoryLength] = def;
+ var newloc = "#" + def;
+ window.location.replace(newloc);
+ } else {
+ //alert(historyHash[historyHash.length-1]);
+ }
+ //setHash(def);
+ setInterval(checkForUrlChange, 50);
+ }
+
+
+ if (browser.firefox || browser.opera)
+ {
+ var reg = new RegExp("#" + def + "$");
+ if (window.location.toString().match(reg)) {
+ } else {
+ var newloc ="#" + def;
+ window.location.replace(newloc);
+ }
+ setInterval(checkForUrlChange, 50);
+ //setHash(def);
+ }
+
+ },
+
+ /* Set the current browser URL; called from inside BrowserManager to propagate
+ * the application state out to the container.
+ */
+ setBrowserURL: function(flexAppUrl, objectId) {
+ if (browser.ie && typeof objectId != "undefined") {
+ currentObjectId = objectId;
+ }
+ //fromIframe = fromIframe || false;
+ //fromFlex = fromFlex || false;
+ //alert("setBrowserURL: " + flexAppUrl);
+ //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ;
+
+ var pos = document.location.href.indexOf('#');
+ var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href;
+ var newUrl = baseUrl + '#' + flexAppUrl;
+
+ if (document.location.href != newUrl && document.location.href + '#' != newUrl) {
+ currentHref = newUrl;
+ addHistoryEntry(baseUrl, newUrl, flexAppUrl);
+ currentHistoryLength = history.length;
+ }
+
+ return false;
+ },
+
+ browserURLChange: function(flexAppUrl) {
+ var objectId = null;
+ if (browser.ie && currentObjectId != null) {
+ objectId = currentObjectId;
+ }
+ pendingURL = '';
+
+ if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) {
+ var pl = getPlayers();
+ for (var i = 0; i < pl.length; i++) {
+ try {
+ pl[i].browserURLChange(flexAppUrl);
+ } catch(e) { }
+ }
+ } else {
+ try {
+ getPlayer(objectId).browserURLChange(flexAppUrl);
+ } catch(e) { }
+ }
+
+ currentObjectId = null;
+ }
+
+ }
+
+})();
+
+// Initialization
+
+// Automated unit testing and other diagnostics
+
+function setURL(url)
+{
+ document.location.href = url;
+}
+
+function backButton()
+{
+ history.back();
+}
+
+function forwardButton()
+{
+ history.forward();
+}
+
+function goForwardOrBackInHistory(step)
+{
+ history.go(step);
+}
+
+//BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); });
+(function(i) {
+ var u =navigator.userAgent;var e=/*@cc_on!@*/false;
+ var st = setTimeout;
+ if(/webkit/i.test(u)){
+ st(function(){
+ var dr=document.readyState;
+ if(dr=="loaded"||dr=="complete"){i()}
+ else{st(arguments.callee,10);}},10);
+ } else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){
+ document.addEventListener("DOMContentLoaded",i,false);
+ } else if(e){
+ (function(){
+ var t=document.createElement('doc:rdy');
+ try{t.doScroll('left');
+ i();t=null;
+ }catch(e){st(arguments.callee,0);}})();
+ } else{
+ window.onload=i;
+ }
+})( function() {BrowserHistory.initialize();} );
29 bin/history/historyFrame.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
+ <META HTTP-EQUIV="Expires" CONTENT="-1">
+ </head>
+ <body>
+ <script>
+ function processUrl()
+ {
+
+ var pos = url.indexOf("?");
+ url = pos != -1 ? url.substr(pos + 1) : "";
+ if (!parent._ie_firstload) {
+ parent.BrowserHistory.setBrowserURL(url);
+ try {
+ parent.BrowserHistory.browserURLChange(url);
+ } catch(e) { }
+ } else {
+ parent._ie_firstload = false;
+ }
+ }
+
+ var url = document.location.href;
+ processUrl();
+ document.write(encodeURIComponent(url));
+ </script>
+ Hidden frame for Browser History support.
+ </body>
+</html>
BIN  bin/playerProductInstall.swf
Binary file not shown
0  example/.irb_history
No changes.
13 example/.irbrc
@@ -0,0 +1,13 @@
+require 'rubygems'
+require 'relaxdb'
+require 'models'
+
+RelaxDB::UuidGenerator.id_length = 4
+RelaxDB.configure :host => "localhost", :port => 5984, :logger => Logger.new(STDOUT)
+RelaxDB.use_db "lrug"
+
+require 'irb/ext/save-history'
+IRB.conf[:SAVE_HISTORY] = 100
+IRB.conf[:HISTORY_FILE] = ".irb_history"
+
+IRB.conf[:PROMPT_MODE] = :SIMPLE
16 example/fuschia.json
@@ -0,0 +1,16 @@
+{
+ "_id" : "fuschia",
+ "classProperty" : "class",
+ "User" : {
+ "fillColor" : "0xff3355cc",
+ "summaryProperty" : "name"
+ },
+ "Post" : {
+ "fillColor" : "0xffcc5533",
+ "summaryProperty" : "contents"
+ },
+ "Comment" : {
+ "fillColor" : "0xff33cc55",
+ "summaryProperty" : "contents"
+ }
+}
BIN  example/fuschia.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 example/fuschia_queries.js
@@ -0,0 +1,30 @@
+// Note that the function names adhere to the RelaxDB convention of design_doc-view_name-func-type
+// CouchDB expects anonymous functions
+
+function fuschia-to_doc-map(doc) {
+ var isDocId = function (str) {
+ var res = /[0-9a-f-]+/(str);
+ return res && str.length >= 32 && str === res[0];
+ };
+
+ for(var n in doc) {
+ var att = doc[n];
+ if (n !== "_id" && isDocId(att)) {
+ emit(att, null);
+ }
+ }
+}
+
+function fuschia-from_doc-map(doc) {
+ var isDocId = function (str) {
+ var res = /[0-9a-f-]+/(str);
+ return res && str.length >= 32 && str === res[0];
+ };
+
+ for(var n in doc) {
+ var att = doc[n];
+ if (n !== "_id" && isDocId(att)) {
+ emit(doc._id, att);
+ }
+ }
+}
33 example/models.rb
@@ -0,0 +1,33 @@
+require 'rubygems'
+require 'relaxdb'
+
+class User < RelaxDB::Document
+
+ property :name
+
+ has_many :comments, :class => "Comment", :known_as => :author
+ has_many :posts, :class => "Post", :known_as => :author
+
+ references :best_friend
+
+end
+
+class Post < RelaxDB::Document
+
+ property :created_at
+ property :contents
+
+ belongs_to :author
+ has_many :comments, :class => "Comment"
+
+end
+
+class Comment < RelaxDB::Document
+
+ property :created_at
+ property :contents
+
+ belongs_to :author
+ belongs_to :post
+
+end
26 example/sample_data.rb
@@ -0,0 +1,26 @@
+require 'rubygems'
+require 'relaxdb'
+require 'models'
+
+RelaxDB.configure :host => "localhost", :port => 5984
+RelaxDB.delete_db "lrug" rescue :ok
+RelaxDB.use_db "lrug"
+
+mal = User.new(:name => "mal").save!
+
+alice = User.new(:name => "alice")
+bob = User.new(:name => "bob", :best_friend => alice)
+alice.best_friend = bob
+RelaxDB.bulk_save(alice, bob)
+
+p = Post.new(:contents => "I heart CouchDB", :author => alice).save!
+p.comments << Comment.new(:contents => "first post", :author => bob)
+p.comments << Comment.new(:contents => "nom nom nom", :author => mal)
+p.save!
+
+p = Post.new(:contents => "in ur couchdbz haxoring ur docs", :author => mal)
+p.comments << Comment.new(:contents => "down with that sort of thing", :author => alice)
+p.comments << Comment.new(:contents => "^^ +1", :author => bob)
+p.save!
+
+RelaxDB.db.put("/fuschia", File.read("fuschia.json"))
351 src/Fuschia.as
@@ -0,0 +1,351 @@
+package {
+ import com.adobe.serialization.json.JSON;
+
+ import flare.display.TextSprite;
+ import flare.vis.Visualization;
+ import flare.vis.controls.ClickControl;
+ import flare.vis.controls.Control;
+ import flare.vis.controls.DragControl;
+ import flare.vis.controls.HoverControl;
+ import flare.vis.data.Data;
+ import flare.vis.data.EdgeSprite;
+ import flare.vis.data.NodeSprite;
+ import flare.vis.data.render.ArrowType;
+ import flare.vis.events.SelectionEvent;
+ import flare.vis.operator.label.Labeler;
+ import flare.vis.operator.layout.ForceDirectedLayout;
+ import flare.vis.operator.layout.Layout;
+
+ import flash.display.Sprite;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.net.URLLoader;
+ import flash.net.URLLoaderDataFormat;
+ import flash.net.URLRequest;
+ import flash.text.TextField;
+ import flash.text.TextFieldType;
+ import flash.text.TextFormat;
+ import flash.utils.Dictionary;
+ import flash.utils.IDataInput;
+
+
+ [SWF(width="1000", height="700", backgroundColor="#ffffff", frameRate="30")]
+ public class Fuschia extends Sprite
+ {
+ private var dbUri:String;
+
+ private var metaData:Object;
+
+ private var nodeMap:Dictionary = new Dictionary();
+ private var edgeMap:Dictionary = new Dictionary();
+
+ private var vis:Visualization;
+ private var data:Data = new Data();
+
+ private var docDetail:TextSprite;
+ private var docSummary:TextSprite;
+
+ private var currentDate:Date;
+
+ private var primaryDocDetail:String;
+ private var primaryDocSummary:String;
+
+ private var dbNameField:TextField;
+ private var docIdField:TextField;
+
+ private var nodeX:Number;
+ private var nodeY:Number;
+
+ public function Fuschia()
+ {
+ createVisualization();
+ }
+
+ private function createVisualization():void
+ {
+ nodeX = stage.stageWidth / 2
+ nodeY = stage.stageHeight / 2;
+
+ vis = new Visualization(data);
+
+ vis.operators.add(createLayout());
+ vis.continuousUpdates = true;
+
+ // Failed to work consistently - no idea why
+// var llr:Labeler = new Labeler("data.label");
+// vis.operators.add(llr);
+
+ vis.controls.add(createHoverControl());
+ vis.controls.add(createClickControl());
+ vis.controls.add(new DragControl(NodeSprite));
+
+ addChild(vis);
+
+ addChild(createDocDetail());
+ addChild(createDocSummary());
+
+ addChild(createDbNameField());
+ addChild(createDbIdField());
+ addChild(createLoadButton());
+ }
+
+ private function createHoverControl():Control
+ {
+ return new HoverControl(NodeSprite,
+ HoverControl.MOVE_AND_RETURN,
+ function(e:SelectionEvent):void {
+ e.node.lineWidth = 2;
+ e.node.lineColor = 0x88ff0000;
+ docDetail.text = docAsFormattedString(e.node.data.doc);
+ docSummary.text = docSummaryStr(e.node.data.doc);
+ },
+ function(e:SelectionEvent):void {
+ e.node.lineWidth = 0;
+ e.node.lineColor = 0xff000000;
+ docDetail.text = primaryDocDetail;
+ docSummary.text = primaryDocSummary;
+ });
+ }
+
+ private function docAsFormattedString(doc:Object):String
+ {
+ var s:String = "";
+ s += "_id" + " : " + doc._id + "\n";
+ s += "_rev" + " : " + doc._rev + "\n";
+ for (var n:Object in doc) {
+ if (n !== "_id" && n !== "_rev") {
+ s += n + " : " + JSON.encode(doc[n]) + "\n";
+ }
+ }
+ return s;
+ }
+
+ private function createClickControl():Control
+ {
+ return new ClickControl(NodeSprite, 1,
+ function(e:SelectionEvent):void {
+ loadDataForDoc(e.node.data.doc._id);
+ }
+ );
+ }
+
+ private function createLayout():Layout
+ {
+ var layout:Layout = new ForceDirectedLayout(true);
+ layout.parameters = {
+ "simulation.dragForce.drag": 0.2,
+ defaultParticleMass: 1,
+ defaultSpringLength: 100,
+ defaultSpringTension: 0.1
+ };
+ return layout;
+ }
+
+ private function createDocDetail():TextSprite
+ {
+ docDetail = new TextSprite();
+ docDetail.textMode = TextSprite.DEVICE;
+ docDetail.textFormat = new TextFormat("Helvetica", 13);
+ docDetail.x = 600;
+ docDetail.y = 5;
+ return docDetail;
+ }
+
+ private function createDocSummary():TextSprite
+ {
+ docSummary = new TextSprite();
+ docSummary.textMode = TextSprite.DEVICE;
+ docSummary.textFormat = new TextFormat("Helvetica", 16);
+ docSummary.x = 5;
+ docSummary.y = 5;
+ return docSummary;
+ }
+
+ private function createDbNameField():TextField
+ {
+ dbNameField = new TextField();
+ dbNameField.type = TextFieldType.INPUT;
+ dbNameField.x = 5;
+ dbNameField.y = 650;
+ dbNameField.height = 15;
+ dbNameField.width = 100;
+ dbNameField.border = true;
+ dbNameField.defaultTextFormat = new TextFormat("Helvetica", 12);
+ dbNameField.text = "db name";
+ return dbNameField;
+ }
+
+ private function createDbIdField():TextField
+ {
+ docIdField = new TextField();
+ docIdField.type = TextFieldType.INPUT;
+ docIdField.x = 125;
+ docIdField.y = 650;
+ docIdField.height = 15;
+ docIdField.width = 200;
+ docIdField.border = true;
+ docIdField.defaultTextFormat = new TextFormat("Helvetica", 12);
+ docIdField.text = "doc id";
+ return docIdField;
+ }
+
+ private function createLoadButton():TextField
+ {
+ var tf:TextField = new TextField();
+ tf.x = 345;
+ tf.y = 650;
+ tf.height = 15;
+ tf.width = 28;
+ var fmt:TextFormat = new TextFormat("Helvetica", 13);
+ tf.defaultTextFormat = fmt;
+ tf.text = "load";
+ tf.addEventListener(MouseEvent.CLICK, function(evt:Event):void {
+ nodeMap = new Dictionary();
+ edgeMap = new Dictionary();
+ data = new Data();
+ vis.data = data;
+ dbUri = dbNameField.text;
+ if (dbUri.charAt(dbUri.length - 1) != "/") {
+ dbUri += "/";
+ }
+
+ loadMetaData(docIdField.text);
+ });
+ return tf;
+ }
+
+ private function loadMetaData(docId:String):void
+ {
+ var metaDataUri:String = dbUri + "fuschia";
+ loadData(metaDataUri, function (doc:Object):void {
+ metaData = doc;
+ loadDataForDoc(docId);
+ });
+ }
+
+ private function displayPrimaryDoc(doc:Object):void
+ {
+ primaryDocDetail = docAsFormattedString(doc);
+ primaryDocSummary = docSummaryStr(doc);
+ docDetail.text = primaryDocDetail;
+ docSummary.text = primaryDocSummary;
+ var ns:NodeSprite = displayDoc(doc);
+ nodeX = ns.x;
+ nodeY = ns.y;
+ }
+
+ private function displayDoc(doc:Object):NodeSprite
+ {
+ var ns:NodeSprite = getNodeSprite(doc._id);
+ ns.data.doc = doc;
+ ns.data.label = docSummaryStr(doc);
+ ns.fillColor = docFillColor(doc);
+ return ns;
+ }
+
+ private function docSummaryStr(doc:Object):String
+ {
+ var summary:String = "";
+ var docMetaData:Object = metaData[doc[metaData.classProperty]];
+ if (docMetaData) {
+ var kp:String = docMetaData.summaryProperty;
+ summary = kp ? doc[kp] : "";
+ }
+ return summary.substring(0, 30);
+ }
+
+ private function docFillColor(doc:Object):Number
+ {
+ var fillColor:Number = 0xffcccccc;
+ var docMetaData:Object = metaData[doc[metaData.classProperty]];
+ if (docMetaData) {
+ fillColor = docMetaData.fillColor ? docMetaData.fillColor : 0xffcccccc;
+ }
+ return fillColor;
+ }
+
+ private function displayInDocs(result:Object):void
+ {
+ for each (var r:Object in result.rows) {
+ var es:EdgeSprite = getEdgeSprite(r.id, r.key);
+ }
+ }
+
+ private function displayOutDocs(result:Object):void
+ {
+ for each (var r:Object in result.rows) {
+ var es:EdgeSprite = getEdgeSprite(r.key, r.value);
+ }
+ }
+
+ private function loadDataForDoc(id:String):void
+ {
+ var docUri:String = dbUri + id;
+ var fromDocUri:String = dbUri + "_view/fuschia/from_doc?key=\"" + id + "\"";
+ var toDocUri:String = dbUri + "_view/fuschia/to_doc?key=\"" + id + "\"";
+
+ loadData(docUri, displayPrimaryDoc);
+ loadData(fromDocUri, displayOutDocs);
+ loadData(toDocUri, displayInDocs);
+ }
+
+ private function loadData(uri:String, func:Function):void
+ {
+ var loader:URLLoader = new URLLoader();
+ loader.dataFormat = URLLoaderDataFormat.BINARY;
+ loader.addEventListener(Event.COMPLETE,
+ function(evt:Event):void {
+ var input:IDataInput = loader.data;
+ var text:String = input.readUTFBytes(input.bytesAvailable);
+ var data:Object = JSON.decode(text) as Object;
+ func(data);
+ }
+ );
+ loader.load(new URLRequest(uri));
+ }
+
+ private function getNodeSprite(docId:Object):NodeSprite
+ {
+ trace("getNode: " + docId);
+ var ns:NodeSprite = nodeMap[docId];
+ if (ns == null) {
+ loadData(dbUri + docId, displayDoc);
+ ns = data.addNode({id:docId});
+ ns.x = nodeX;
+ ns.y = nodeY;
+ nodeMap[docId] = ns;
+ }
+ return ns;
+ }
+
+ private function getEdgeSprite(source_id:Object, target_id:Object):EdgeSprite
+ {
+ var edgeId:String = source_id + "_" + target_id;
+ var otherEdgeId:String = target_id + "_" + source_id;
+ trace("getEdge: " + edgeId);
+
+ var es:EdgeSprite = edgeMap[edgeId];
+ if (es == null) {
+ trace("adding edge: " + edgeId);
+ var source:NodeSprite = getNodeSprite(source_id);
+ var target:NodeSprite = getNodeSprite(target_id);
+ es = data.addEdgeFor(source, target, true);
+ es.arrowType = ArrowType.TRIANGLE;
+ es.arrowWidth = 15;
+ edgeMap[edgeId] = es;
+ }
+
+ // Remove the other edge as a workaround for apparently ineffectual
+ // sort orders - defining a z-index and sorting on it only has the
+ // desired effect on the first draw
+ var otherEs:EdgeSprite = edgeMap[otherEdgeId];
+ if (otherEs != null) {
+ trace("removing edge: " + otherEdgeId);
+ data.removeEdge(otherEs);
+ delete edgeMap[otherEdgeId];
+ }
+ return es;
+ }
+
+ }
+}
29 src/fuschia_queries.js
@@ -0,0 +1,29 @@
+// _design/fuschia.views.to_doc.map
+function (doc) {
+ var isDocId = function (str) {
+ var res = /[0-9a-f-]+/(str);
+ return res && str.length >= 32 && str === res[0];
+ };
+
+ for(var n in doc) {
+ var att = doc[n];
+ if (n !== "_id" && isDocId(att)) {
+ emit(att, null);
+ }
+ }
+}
+
+// _design/fuschia.views.from_doc.map
+function (doc) {
+ var isDocId = function (str) {
+ var res = /[0-9a-f-]+/(str);
+ return res && str.length >= 32 && str === res[0];
+ };
+
+ for(var n in doc) {
+ var att = doc[n];
+ if (n !== "_id" && isDocId(att)) {
+ emit(doc._id, att);
+ }
+ }
+}
20 upload_binaries.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+DB_NAME=your_db_name
+
+DESIGN_DOC=localhost:5984/${DB_NAME}/_design%2Ffuschia
+
+JS='Content-Type:application/x-javascript'
+HTML='Content-Type:text/html'
+SWF='Content-Type:application/x-shockwave-flash'
+CSS='Content-Type:text/css'
+
+# Will intentionally fail if a design doc named fuschia already exists
+REV=`curl -s -T bin/fuschia_queries.json $DESIGN_DOC | egrep -o '[0-9]+'`
+REV=`curl -s -H $JS -T bin/AC_OETags.js ${DESIGN_DOC}/AC_OETags.js?rev=${REV} | egrep -o '[0-9]+'`
+REV=`curl -s -H $HTML -T bin/Fuschia.html ${DESIGN_DOC}/Fuschia.html?rev=${REV} | egrep -o '[0-9]+'`
+REV=`curl -s -H $SWF -T bin/Fuschia.swf ${DESIGN_DOC}/Fuschia.swf?rev=${REV} | egrep -o '[0-9]+'`
+REV=`curl -s -H $CSS -T bin/history/history.css ${DESIGN_DOC}/history/history.css?rev=${REV} | egrep -o '[0-9]+'`
+REV=`curl -s -H $JS -T bin/history/history.js ${DESIGN_DOC}/history/history.js?rev=${REV} | egrep -o '[0-9]+'`
+REV=`curl -s -H $HTML -T bin/history/historyFrame.html ${DESIGN_DOC}/history/historyFrame.html?rev=${REV} | egrep -o '[0-9]+'`
+REV=`curl -s -H $SWF -T bin/playerProductInstall.swf ${DESIGN_DOC}/playerProductInstall.swf?rev=${REV} | egrep -o '[0-9]+'`
Please sign in to comment.
Something went wrong with that request. Please try again.