diff options
author | Ondrej Zara <ondrej.zara@firma.seznam.cz> | 2015-06-12 09:30:51 +0200 |
---|---|---|
committer | Ondrej Zara <ondrej.zara@firma.seznam.cz> | 2015-06-12 09:30:51 +0200 |
commit | b3bf2bd8ecc9bd175793f67c46a8319b7d67174c (patch) | |
tree | e74aa005861dbd62381cfce3aac1d1acba3728c0 | |
parent | c5a07ea2b037cc880225defc58ead88c916fa066 (diff) | |
download | wwwsqldesigner-b3bf2bd8ecc9bd175793f67c46a8319b7d67174c.zip wwwsqldesigner-b3bf2bd8ecc9bd175793f67c46a8319b7d67174c.tar.gz wwwsqldesigner-b3bf2bd8ecc9bd175793f67c46a8319b7d67174c.tar.bz2 |
js modularized
-rwxr-xr-x | index.html | 31 | ||||
-rw-r--r-- | js/globals.js | 31 | ||||
-rw-r--r-- | js/io.js | 495 | ||||
-rw-r--r-- | js/key.js | 74 | ||||
-rw-r--r-- | js/keymanager.js | 204 | ||||
-rw-r--r-- | js/map.js | 119 | ||||
-rw-r--r-- | js/options.js | 107 | ||||
-rw-r--r-- | js/relation.js | 158 | ||||
-rw-r--r-- | js/row.js | 425 | ||||
-rw-r--r-- | js/rowmanager.js | 190 | ||||
-rw-r--r-- | js/rubberband.js | 49 | ||||
-rw-r--r-- | js/table.js | 362 | ||||
-rw-r--r-- | js/tablemanager.js | 211 | ||||
-rw-r--r-- | js/visual.js | 42 | ||||
-rw-r--r-- | js/window.js | 95 | ||||
-rwxr-xr-x | js/wwwsqldesigner.js | 2576 |
16 files changed, 2585 insertions, 2584 deletions
@@ -1,22 +1,37 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<!doctype html> <!-- - WWW SQL Designer, (C) 2005-2012 Ondrej Zara, ondras@zarovi.cz + WWW SQL Designer, (C) 2005-2015 Ondrej Zara, ondras@zarovi.cz Version: 2.7 See license.txt for licencing information. --> -<html xmlns="http://www.w3.org/1999/xhtml"> +<html> <head> <title>WWW SQL Designer</title> <meta name="viewport" content="initial-scale=1,maximum-scale=1" /> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <link rel="stylesheet" type="text/css" href="styles/style.css" media="all" /> + <meta charset="utf-8" /> + <link rel="stylesheet" href="styles/style.css" media="all" /> <!--[if IE 6]><link rel="stylesheet" type="text/css" href="styles/ie6.css" /><![endif]--> <!--[if IE 7]><link rel="stylesheet" type="text/css" href="styles/ie7.css" /><![endif]--> <link rel="stylesheet" href="styles/print.css" type="text/css" media="print" /> - <script type="text/javascript" src="js/oz.js"></script> - <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dropbox.js/0.10.2/dropbox.min.js"></script> - <script type="text/javascript" src="js/wwwsqldesigner.js"></script> + + <script src="js/oz.js"></script> + <script src="js/config.js"></script> + <script src="js/globals.js"></script> + <script src="js/visual.js"></script> + <script src="js/row.js"></script> + <script src="js/table.js"></script> + <script src="js/relation.js"></script> + <script src="js/key.js"></script> + <script src="js/rubberband.js"></script> + <script src="js/map.js"></script> + <script src="js/io.js"></script> + <script src="js/tablemanager.js"></script> + <script src="js/rowmanager.js"></script> + <script src="js/keymanager.js"></script> + <script src="js/window.js"></script> + <script src="js/options.js"></script> + <script src="js/wwwsqldesigner.js"></script> </head> <body> diff --git a/js/globals.js b/js/globals.js new file mode 100644 index 0000000..1d59315 --- /dev/null +++ b/js/globals.js @@ -0,0 +1,31 @@ +/* -------------------- configuration -------------------- */ + +/* + * The key below needs to be set individually by you if you want to use the Dropbox load/save feature. + * To do that, first sign up with Dropbox (may require a specific developer / SDK sign-up), go to + * https://www.dropbox.com/developers/apps and use "Create app" to add a new app. Call it, for instance, + * "wwwsqldesigner", and give it the "App Folder" permission. Unter "OAuth 2", "Redirect URIs", add + * the URL to the "dropbox-oauth-receiver.html" file on your server. E.g, if you install wwwsqldesigner + * on your local web server under "http://localhost/sqldesigner/", then add + * http://localhost/sqldesigner/dropbox-oauth-receiver.html as a Redirection URI. + * Copy the shown "App key" and paste it here below: + */ +var dropboxAppKey = null; // "your app key"; + + +/* -------------------- globals -------------------- */ + +function _(str) { /* getText */ + if (!(str in window.LOCALE)) { return str; } + return window.LOCALE[str]; +} + +if (typeof String.prototype.endsWith !== 'function') { + String.prototype.endsWith = function(suffix) { + return this.indexOf(suffix, this.length - suffix.length) !== -1; + }; +} + +var DATATYPES = false; +var LOCALE = {}; +var SQL = {}; diff --git a/js/io.js b/js/io.js new file mode 100644 index 0000000..1fdc3c4 --- /dev/null +++ b/js/io.js @@ -0,0 +1,495 @@ +/* --------------------- io ------------ */ + +SQL.IO = OZ.Class(); + +SQL.IO.prototype.init = function(owner) { + this.owner = owner; + this._name = ""; /* last used name with server load/save */ + this.lastUsedName = ""; /* last used name with local storage or dropbox load/save */ + this.dom = { + container:OZ.$("io") + }; + + var ids = ["saveload","clientlocalsave", "clientsave", "clientlocalload", "clientlocallist","clientload", "clientsql", + "dropboxsave", "dropboxload", "dropboxlist", + "quicksave", "serversave", "serverload", + "serverlist", "serverimport"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + this.dom[id] = elm; + elm.value = _(id); + } + + this.dom.quicksave.value += " (F2)"; + + var ids = ["client","server","output","backendlabel"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + elm.innerHTML = _(id); + } + + this.dom.ta = OZ.$("textarea"); + this.dom.backend = OZ.$("backend"); + + this.dom.container.parentNode.removeChild(this.dom.container); + this.dom.container.style.visibility = ""; + + this.saveresponse = this.bind(this.saveresponse); + this.loadresponse = this.bind(this.loadresponse); + this.listresponse = this.bind(this.listresponse); + this.importresponse = this.bind(this.importresponse); + + OZ.Event.add(this.dom.saveload, "click", this.bind(this.click)); + OZ.Event.add(this.dom.clientlocalsave, "click", this.bind(this.clientlocalsave)); + OZ.Event.add(this.dom.clientsave, "click", this.bind(this.clientsave)); + OZ.Event.add(this.dom.clientlocalload, "click", this.bind(this.clientlocalload)); + OZ.Event.add(this.dom.clientlocallist, "click", this.bind(this.clientlocallist)); + OZ.Event.add(this.dom.clientload, "click", this.bind(this.clientload)); + OZ.Event.add(this.dom.dropboxload, "click", this.bind(this.dropboxload)); + OZ.Event.add(this.dom.dropboxsave, "click", this.bind(this.dropboxsave)); + OZ.Event.add(this.dom.dropboxlist, "click", this.bind(this.dropboxlist)); + OZ.Event.add(this.dom.clientsql, "click", this.bind(this.clientsql)); + OZ.Event.add(this.dom.quicksave, "click", this.bind(this.quicksave)); + OZ.Event.add(this.dom.serversave, "click", this.bind(this.serversave)); + OZ.Event.add(this.dom.serverload, "click", this.bind(this.serverload)); + OZ.Event.add(this.dom.serverlist, "click", this.bind(this.serverlist)); + OZ.Event.add(this.dom.serverimport, "click", this.bind(this.serverimport)); + OZ.Event.add(document, "keydown", this.bind(this.press)); + this.build(); + + this.dropBoxInit (); +} + +SQL.IO.prototype.build = function() { + OZ.DOM.clear(this.dom.backend); + + var bs = CONFIG.AVAILABLE_BACKENDS; + var be = CONFIG.DEFAULT_BACKEND; + var r = window.location.search.substring(1).match(/backend=([^&]*)/); + if (r) { + req = r[1]; + if (bs.indexOf(req) != -1) { + be = req; + } + } + for (var i=0;i<bs.length;i++) { + var o = OZ.DOM.elm("option"); + o.value = bs[i]; + o.innerHTML = bs[i]; + this.dom.backend.appendChild(o); + if (bs[i] == be) { this.dom.backend.selectedIndex = i; } + } +} + +SQL.IO.prototype.click = function() { /* open io dialog */ + this.build(); + this.dom.ta.value = ""; + this.dom.clientsql.value = _("clientsql") + " (" + window.DATATYPES.getAttribute("db") + ")"; + this.owner.window.open(_("saveload"),this.dom.container); +} + +SQL.IO.prototype.fromXMLText = function(xml) { + try { + if (window.DOMParser) { + var parser = new DOMParser(); + var xmlDoc = parser.parseFromString(xml, "text/xml"); + } else if (window.ActiveXObject || "ActiveXObject" in window) { + var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.loadXML(xml); + } else { + throw new Error("No XML parser available."); + } + } catch(e) { + alert(_("xmlerror")+': '+e.message); + return; + } + this.fromXML(xmlDoc); +} + +SQL.IO.prototype.fromXML = function(xmlDoc) { + if (!xmlDoc || !xmlDoc.documentElement) { + alert(_("xmlerror")+': Null document'); + return false; + } + this.owner.fromXML(xmlDoc.documentElement); + this.owner.window.close(); + return true; +} + +SQL.IO.prototype.clientsave = function() { + var xml = this.owner.toXML(); + this.dom.ta.value = xml; +} + +SQL.IO.prototype.clientload = function() { + var xml = this.dom.ta.value; + if (!xml) { + alert(_("empty")); + return; + } + + this.fromXMLText(xml); +} + +SQL.IO.prototype.promptName = function(title, suffix) { + var lastUsedName = this.owner.getOption("lastUsedName") || this.lastUsedName; + var name = prompt(_(title), lastUsedName); + if (!name) { return null; } + if (suffix && name.endsWith(suffix)) { + // remove suffix from name + name = name.substr(0, name.length-4); + } + this.owner.setOption("lastUsedName", name); + this.lastUsedName = name; // save this also in variable in case cookies are disabled + return name; +} + +SQL.IO.prototype.clientlocalsave = function() { + if (!window.localStorage) { + alert("Sorry, your browser does not seem to support localStorage."); + return; + } + + var xml = this.owner.toXML(); + if (xml.length >= (5*1024*1024)/2) { /* this is a very big db structure... */ + alert("Warning: your database structure is above 5 megabytes in size, this is above the localStorage single key limit allowed by some browsers, example Mozilla Firefox 10"); + return; + } + + var key = this.promptName("serversaveprompt"); + if (!key) { return; } + key = "wwwsqldesigner_databases_" + (key || "default"); + + try { + localStorage.setItem(key, xml); + if (localStorage.getItem(key) != xml) { throw new Error("Content verification failed"); } + } catch (e) { + alert("Error saving database structure to localStorage! ("+e.message+")"); + } +} + +SQL.IO.prototype.clientlocalload = function() { + if (!window.localStorage) { + alert("Sorry, your browser does not seem to support localStorage."); + return; + } + + var key = this.promptName("serverloadprompt"); + key = "wwwsqldesigner_databases_" + (key || "default"); + + try { + var xml = localStorage.getItem(key); + if (!xml) { throw new Error("No data available"); } + } catch (e) { + alert("Error loading database structure from localStorage! ("+e.message+")"); + return; + } + + this.fromXMLText(xml); +} + +SQL.IO.prototype.clientlocallist = function() { + if (!window.localStorage) { + alert("Sorry, your browser does not seem to support localStorage."); + return; + } + + /* --- Define some useful vars --- */ + var baseKeysName = "wwwsqldesigner_databases_"; + var localLen = localStorage.length; + var data = ""; + var schemasFound = false; + var code = 200; + + /* --- work --- */ + try { + for (var i = 0; i< localLen; ++i) { + var key = localStorage.key(i); + if((new RegExp(baseKeysName)).test(key)) { + var result = key.substring(baseKeysName.length); + schemasFound = true; + data += result + "\n"; + } + } + if (!schemasFound) { + throw new Error("No data available"); + } + } catch (e) { + alert("Error loading database names from localStorage! ("+e.message+")"); + return; + } + this.listresponse(data, code); +} + +/* ------------------------- Dropbox start ------------------------ */ + +/* + * The following code uses this lib: https://github.com/dropbox/dropbox-js + */ + +SQL.IO.prototype.dropBoxInit = function() +{ + if (dropboxAppKey) { + this.dropboxClient = new Dropbox.Client({ key: dropboxAppKey }); +} else { + this.dropboxClient = null; + + // Hide the Dropbox buttons and divider + var elems = document.querySelectorAll("[id^=dropbox]"); // gets all tags whose id start with "dropbox" + [].slice.call(elems).forEach( + function(elem) { elem.style.display = "none"; } + ); + } +} + +SQL.IO.prototype.showDropboxError = function(error) { + var prefix = _("Dropbox error")+": "; + var msg = error.status; + + switch (error.status) { + case Dropbox.ApiError.INVALID_TOKEN: + // If you're using dropbox.js, the only cause behind this error is that + // the user token expired. + // Get the user through the authentication flow again. + msg = _("Invalid Token (expired)"); + break; + + case Dropbox.ApiError.NOT_FOUND: + // The file or folder you tried to access is not in the user's Dropbox. + // Handling this error is specific to your application. + msg = _("File not found"); + break; + + case Dropbox.ApiError.OVER_QUOTA: + // The user is over their Dropbox quota. + // Tell them their Dropbox is full. Refreshing the page won't help. + msg = _("Dropbox is full"); + break; + + case Dropbox.ApiError.RATE_LIMITED: + // Too many API requests. Tell the user to try again later. + // Long-term, optimize your code to use fewer API calls. + break; + + case Dropbox.ApiError.NETWORK_ERROR: + // An error occurred at the XMLHttpRequest layer. + // Most likely, the user's network connection is down. + // API calls will not succeed until the user gets back online. + msg = _("Network error"); + break; + + case Dropbox.ApiError.INVALID_PARAM: + case Dropbox.ApiError.OAUTH_ERROR: + case Dropbox.ApiError.INVALID_METHOD: + default: + // Caused by a bug in dropbox.js, in your application, or in Dropbox. + // Tell the user an error occurred, ask them to refresh the page. + } + + alert (prefix+msg); +}; + +SQL.IO.prototype.showDropboxAuthenticate = function() { + if (!this.dropboxClient) return false; + + // We want to use a popup window for authentication as the default redirection won't work for us as it'll make us lose our schema data + this.dropboxClient.authDriver(new Dropbox.AuthDriver.Popup({ receiverUrl: "dropbox-oauth-receiver.html" })); + + // Now let's authenticate us + var success = false; + this.dropboxClient.authenticate( function(error, client) { + if (error) { + this.showDropboxError(error); + return; + } + success = true; + return; + }); + return success; +} + +SQL.IO.prototype.dropboxsave = function() { + if (!this.showDropboxAuthenticate()) return; + + var key = this.promptName("serversaveprompt", ".xml"); + if (!key) { return; } + var filename = (key || "default") + ".xml"; + + var sql_io = this; + sql_io.listresponse("Saving...", 200); + var xml = this.owner.toXML(); + this.dropboxClient.writeFile(filename, xml, function(error, stat) { + if (error) { + sql_io.listresponse("", 200); + return this.showDropboxError(error); + } + sql_io.listresponse(filename+" "+_("was saved to Dropbox"), 200); + }); +} + +SQL.IO.prototype.dropboxload = function() { + if (!this.showDropboxAuthenticate()) return; + + var key = this.promptName("serverloadprompt", ".xml"); + if (!key) { return; } + var filename = (key || "default") + ".xml"; + + var sql_io = this; + sql_io.listresponse("Loading...", 200); + this.dropboxClient.readFile(filename, function(error, data) { + sql_io.listresponse("", 200); + if (error) { + return this.showDropboxError(error); + } + sql_io.fromXMLText(data); + }); +} + +SQL.IO.prototype.dropboxlist = function() { + if (!this.showDropboxAuthenticate()) return; + + var sql_io = this; + sql_io.listresponse("Loading...", 200); + this.dropboxClient.readdir("/", function(error, entries) { + if (error) { + sql_io.listresponse("", 200); + return this.showDropboxError(error); + } + var data = entries.join("\n")+"\n"; + sql_io.listresponse(data, 200); + }); +} + + +/* ------------------------- Dropbox end ------------------------ */ + +SQL.IO.prototype.clientsql = function() { + var bp = this.owner.getOption("staticpath"); + var path = bp + "db/"+window.DATATYPES.getAttribute("db")+"/output.xsl"; + this.owner.window.showThrobber(); + OZ.Request(path, this.bind(this.finish), {xml:true}); +} + +SQL.IO.prototype.finish = function(xslDoc) { + this.owner.window.hideThrobber(); + var xml = this.owner.toXML(); + var sql = ""; + try { + if (window.XSLTProcessor && window.DOMParser) { + var parser = new DOMParser(); + var xmlDoc = parser.parseFromString(xml, "text/xml"); + var xsl = new XSLTProcessor(); + xsl.importStylesheet(xslDoc); + var result = xsl.transformToDocument(xmlDoc); + sql = result.documentElement.textContent; + } else if (window.ActiveXObject || "ActiveXObject" in window) { + var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.loadXML(xml); + sql = xmlDoc.transformNode(xslDoc); + } else { + throw new Error("No XSLT processor available"); + } + } catch(e) { + alert(_("xmlerror")+': '+e.message); + return; + } + this.dom.ta.value = sql.trim(); +} + +SQL.IO.prototype.serversave = function(e, keyword) { + var name = keyword || prompt(_("serversaveprompt"), this._name); + if (!name) { return; } + this._name = name; + var xml = this.owner.toXML(); + var bp = this.owner.getOption("xhrpath"); + var url = bp + "backend/"+this.dom.backend.value+"/?action=save&keyword="+encodeURIComponent(name); + var h = {"Content-type":"application/xml"}; + this.owner.window.showThrobber(); + this.owner.setTitle(name); + OZ.Request(url, this.saveresponse, {xml:true, method:"post", data:xml, headers:h}); +} + +SQL.IO.prototype.quicksave = function(e) { + this.serversave(e, this._name); +} + +SQL.IO.prototype.serverload = function(e, keyword) { + var name = keyword || prompt(_("serverloadprompt"), this._name); + if (!name) { return; } + this._name = name; + var bp = this.owner.getOption("xhrpath"); + var url = bp + "backend/"+this.dom.backend.value+"/?action=load&keyword="+encodeURIComponent(name); + this.owner.window.showThrobber(); + this.name = name; + OZ.Request(url, this.loadresponse, {xml:true}); +} + +SQL.IO.prototype.serverlist = function(e) { + var bp = this.owner.getOption("xhrpath"); + var url = bp + "backend/"+this.dom.backend.value+"/?action=list"; + this.owner.window.showThrobber(); + OZ.Request(url, this.listresponse); +} + +SQL.IO.prototype.serverimport = function(e) { + var name = prompt(_("serverimportprompt"), ""); + if (!name) { return; } + var bp = this.owner.getOption("xhrpath"); + var url = bp + "backend/"+this.dom.backend.value+"/?action=import&database="+name; + this.owner.window.showThrobber(); + OZ.Request(url, this.importresponse, {xml:true}); +} + +SQL.IO.prototype.check = function(code) { + switch (code) { + case 201: + case 404: + case 500: + case 501: + case 503: + var lang = "http"+code; + this.dom.ta.value = _("httpresponse")+": "+_(lang); + return false; + break; + default: return true; + } +} + +SQL.IO.prototype.saveresponse = function(data, code) { + this.owner.window.hideThrobber(); + this.check(code); +} + +SQL.IO.prototype.loadresponse = function(data, code) { + this.owner.window.hideThrobber(); + if (!this.check(code)) { return; } + this.fromXML(data); + this.owner.setTitle(this.name); +} + +SQL.IO.prototype.listresponse = function(data, code) { + this.owner.window.hideThrobber(); + if (!this.check(code)) { return; } + this.dom.ta.value = data; +} + +SQL.IO.prototype.importresponse = function(data, code) { + this.owner.window.hideThrobber(); + if (!this.check(code)) { return; } + if (this.fromXML(data)) { + this.owner.alignTables(); + } +} + +SQL.IO.prototype.press = function(e) { + switch (e.keyCode) { + case 113: + if (OZ.opera) { + e.preventDefault(); + } + this.quicksave(e); + break; + } +} diff --git a/js/key.js b/js/key.js new file mode 100644 index 0000000..3b39d42 --- /dev/null +++ b/js/key.js @@ -0,0 +1,74 @@ +/* --------------------- db index ------------ */ + +SQL.Key = OZ.Class().extend(SQL.Visual); + +SQL.Key.prototype.init = function(owner, type, name) { + this.owner = owner; + this.rows = []; + this.type = type || "INDEX"; + this.name = name || ""; + SQL.Visual.prototype.init.apply(this); +} + +SQL.Key.prototype.setName = function(n) { + this.name = n; +} + +SQL.Key.prototype.getName = function() { + return this.name; +} + +SQL.Key.prototype.setType = function(t) { + if (!t) { return; } + this.type = t; + for (var i=0;i<this.rows.length;i++) { this.rows[i].redraw(); } +} + +SQL.Key.prototype.getType = function() { + return this.type; +} + +SQL.Key.prototype.addRow = function(r) { + if (r.owner != this.owner) { return; } + this.rows.push(r); + r.addKey(this); +} + +SQL.Key.prototype.removeRow = function(r) { + var idx = this.rows.indexOf(r); + if (idx == -1) { return; } + r.removeKey(this); + this.rows.splice(idx,1); +} + +SQL.Key.prototype.destroy = function() { + for (var i=0;i<this.rows.length;i++) { + this.rows[i].removeKey(this); + } +} + +SQL.Key.prototype.getLabel = function() { + return this.name || this.type; +} + +SQL.Key.prototype.toXML = function() { + var xml = ""; + xml += '<key type="'+this.getType()+'" name="'+this.getName()+'">\n'; + for (var i=0;i<this.rows.length;i++) { + var r = this.rows[i]; + xml += '<part>'+r.getTitle()+'</part>\n'; + } + xml += '</key>\n'; + return xml; +} + +SQL.Key.prototype.fromXML = function(node) { + this.setType(node.getAttribute("type")); + this.setName(node.getAttribute("name")); + var parts = node.getElementsByTagName("part"); + for (var i=0;i<parts.length;i++) { + var name = parts[i].firstChild.nodeValue; + var row = this.owner.findNamedRow(name); + this.addRow(row); + } +} diff --git a/js/keymanager.js b/js/keymanager.js new file mode 100644 index 0000000..e053a0a --- /dev/null +++ b/js/keymanager.js @@ -0,0 +1,204 @@ +/* ----------------- key manager ---------- */ + +SQL.KeyManager = OZ.Class(); + +SQL.KeyManager.prototype.init = function(owner) { + this.owner = owner; + this.dom = { + container:OZ.$("keys") + } + this.build(); +} + +SQL.KeyManager.prototype.build = function() { + this.dom.list = OZ.$("keyslist"); + this.dom.type = OZ.$("keytype"); + this.dom.name = OZ.$("keyname"); + this.dom.left = OZ.$("keyleft"); + this.dom.right = OZ.$("keyright"); + this.dom.fields = OZ.$("keyfields"); + this.dom.avail = OZ.$("keyavail"); + this.dom.listlabel = OZ.$("keyslistlabel"); + + var ids = ["keyadd","keyremove"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + this.dom[id] = elm; + elm.value = _(id); + } + + var ids = ["keyedit","keytypelabel","keynamelabel","keyfieldslabel","keyavaillabel"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + elm.innerHTML = _(id); + } + + var types = ["PRIMARY","INDEX","UNIQUE","FULLTEXT"]; + OZ.DOM.clear(this.dom.type); + for (var i=0;i<types.length;i++) { + var o = OZ.DOM.elm("option"); + o.innerHTML = types[i]; + o.value = types[i]; + this.dom.type.appendChild(o); + } + + this.purge = this.bind(this.purge); + + OZ.Event.add(this.dom.list, "change", this.bind(this.listchange)); + OZ.Event.add(this.dom.type, "change", this.bind(this.typechange)); + OZ.Event.add(this.dom.name, "keyup", this.bind(this.namechange)); + OZ.Event.add(this.dom.keyadd, "click", this.bind(this.add)); + OZ.Event.add(this.dom.keyremove, "click", this.bind(this.remove)); + OZ.Event.add(this.dom.left, "click", this.bind(this.left)); + OZ.Event.add(this.dom.right, "click", this.bind(this.right)); + + this.dom.container.parentNode.removeChild(this.dom.container); +} + +SQL.KeyManager.prototype.listchange = function(e) { + this.switchTo(this.dom.list.selectedIndex); +} + +SQL.KeyManager.prototype.typechange = function(e) { + this.key.setType(this.dom.type.value); + this.redrawListItem(); +} + +SQL.KeyManager.prototype.namechange = function(e) { + this.key.setName(this.dom.name.value); + this.redrawListItem(); +} + +SQL.KeyManager.prototype.add = function(e) { + var type = (this.table.keys.length ? "INDEX" : "PRIMARY"); + this.table.addKey(type); + this.sync(this.table); + this.switchTo(this.table.keys.length-1); +} + +SQL.KeyManager.prototype.remove = function(e) { + var index = this.dom.list.selectedIndex; + if (index == -1) { return; } + var r = this.table.keys[index]; + this.table.removeKey(r); + this.sync(this.table); +} + +SQL.KeyManager.prototype.purge = function() { /* remove empty keys */ + for (var i=this.table.keys.length-1;i>=0;i--) { + var k = this.table.keys[i]; + if (!k.rows.length) { this.table.removeKey(k); } + } +} + +SQL.KeyManager.prototype.sync = function(table) { /* sync content with given table */ + this.table = table; + this.dom.listlabel.innerHTML = _("keyslistlabel").replace(/%s/,table.getTitle()); + + OZ.DOM.clear(this.dom.list); + for (var i=0;i<table.keys.length;i++) { + var k = table.keys[i]; + var o = OZ.DOM.elm("option"); + this.dom.list.appendChild(o); + var str = (i+1)+": "+k.getLabel(); + o.innerHTML = str; + } + if (table.keys.length) { + this.switchTo(0); + } else { + this.disable(); + } +} + +SQL.KeyManager.prototype.redrawListItem = function() { + var index = this.table.keys.indexOf(this.key); + this.option.innerHTML = (index+1)+": "+this.key.getLabel(); +} + +SQL.KeyManager.prototype.switchTo = function(index) { /* show Nth key */ + this.enable(); + var k = this.table.keys[index]; + this.key = k; + this.option = this.dom.list.getElementsByTagName("option")[index]; + + this.dom.list.selectedIndex = index; + this.dom.name.value = k.getName(); + + var opts = this.dom.type.getElementsByTagName("option"); + for (var i=0;i<opts.length;i++) { + if (opts[i].value == k.getType()) { this.dom.type.selectedIndex = i; } + } + + OZ.DOM.clear(this.dom.fields); + for (var i=0;i<k.rows.length;i++) { + var o = OZ.DOM.elm("option"); + o.innerHTML = k.rows[i].getTitle(); + o.value = o.innerHTML; + this.dom.fields.appendChild(o); + } + + OZ.DOM.clear(this.dom.avail); + for (var i=0;i<this.table.rows.length;i++) { + var r = this.table.rows[i]; + if (k.rows.indexOf(r) != -1) { continue; } + var o = OZ.DOM.elm("option"); + o.innerHTML = r.getTitle(); + o.value = o.innerHTML; + this.dom.avail.appendChild(o); + } +} + +SQL.KeyManager.prototype.disable = function() { + OZ.DOM.clear(this.dom.fields); + OZ.DOM.clear(this.dom.avail); + this.dom.keyremove.disabled = true; + this.dom.left.disabled = true; + this.dom.right.disabled = true; + this.dom.list.disabled = true; + this.dom.name.disabled = true; + this.dom.type.disabled = true; + this.dom.fields.disabled = true; + this.dom.avail.disabled = true; +} + +SQL.KeyManager.prototype.enable = function() { + this.dom.keyremove.disabled = false; + this.dom.left.disabled = false; + this.dom.right.disabled = false; + this.dom.list.disabled = false; + this.dom.name.disabled = false; + this.dom.type.disabled = false; + this.dom.fields.disabled = false; + this.dom.avail.disabled = false; +} + +SQL.KeyManager.prototype.left = function(e) { /* add field to index */ + var opts = this.dom.avail.getElementsByTagName("option"); + for (var i=0;i<opts.length;i++) { + var o = opts[i]; + if (o.selected) { + var row = this.table.findNamedRow(o.value); + this.key.addRow(row); + } + } + this.switchTo(this.dom.list.selectedIndex); +} + +SQL.KeyManager.prototype.right = function(e) { /* remove field from index */ + var opts = this.dom.fields.getElementsByTagName("option"); + for (var i=0;i<opts.length;i++) { + var o = opts[i]; + if (o.selected) { + var row = this.table.findNamedRow(o.value); + this.key.removeRow(row); + } + } + this.switchTo(this.dom.list.selectedIndex); +} + +SQL.KeyManager.prototype.open = function(table) { + this.sync(table); + this.owner.window.open(_("tablekeys"),this.dom.container,this.purge); +} diff --git a/js/map.js b/js/map.js new file mode 100644 index 0000000..8a82646 --- /dev/null +++ b/js/map.js @@ -0,0 +1,119 @@ +/* --------------------- minimap ------------ */ + +SQL.Map = OZ.Class().extend(SQL.Visual); + +SQL.Map.prototype.init = function(owner) { + this.owner = owner; + SQL.Visual.prototype.init.apply(this); + this.dom.container = OZ.$("minimap"); + this.width = this.dom.container.offsetWidth - 2; + this.height = this.dom.container.offsetHeight - 2; + + this.dom.port = OZ.DOM.elm("div",{className:"port", zIndex:1}); + this.dom.container.appendChild(this.dom.port); + this.sync = this.bind(this.sync); + + this.flag = false; + this.sync(); + + OZ.Event.add(window, "resize", this.sync); + OZ.Event.add(window, "scroll", this.sync); + OZ.Event.add(this.dom.container, "mousedown", this.bind(this.down)); + OZ.Event.add(this.dom.container, "touchstart", this.bind(this.down)); + OZ.Event.add(this.dom.container, "touchmove", OZ.Event.prevent); +} + +SQL.Map.prototype.down = function(e) { /* mousedown - move view and start drag */ + this.flag = true; + this.dom.container.style.cursor = "move"; + var pos = OZ.DOM.pos(this.dom.container); + + this.x = Math.round(pos[0] + this.l + this.w/2); + this.y = Math.round(pos[1] + this.t + this.h/2); + this.move(e); + + if (e.type == "touchstart") { + var eventMove = "touchmove"; + var eventUp = "touchend"; + } else { + var eventMove = "mousemove"; + var eventUp = "mouseup"; + } + + this.documentMove = OZ.Event.add(document, eventMove, this.bind(this.move)); + this.documentUp = OZ.Event.add(document, eventUp, this.bind(this.up)); +} + +SQL.Map.prototype.move = function(e) { /* mousemove */ + if (!this.flag) { return; } + OZ.Event.prevent(e); + + if (e.type.match(/touch/)) { + if (e.touches.length > 1) { return; } + var event = e.touches[0]; + } else { + var event = e; + } + + var dx = event.clientX - this.x; + var dy = event.clientY - this.y; + if (this.l + dx < 0) { dx = -this.l; } + if (this.t + dy < 0) { dy = -this.t; } + if (this.l + this.w + 4 + dx > this.width) { dx = this.width - 4 - this.l - this.w; } + if (this.t + this.h + 4 + dy > this.height) { dy = this.height - 4 - this.t - this.h; } + + + this.x += dx; + this.y += dy; + + this.l += dx; + this.t += dy; + + var coefX = this.width / this.owner.width; + var coefY = this.height / this.owner.height; + var left = this.l / coefX; + var top = this.t / coefY; + + if (OZ.webkit) { + document.body.scrollLeft = Math.round(left); + document.body.scrollTop = Math.round(top); + } else { + document.documentElement.scrollLeft = Math.round(left); + document.documentElement.scrollTop = Math.round(top); + } + + this.redraw(); +} + +SQL.Map.prototype.up = function(e) { /* mouseup */ + this.flag = false; + this.dom.container.style.cursor = ""; + OZ.Event.remove(this.documentMove); + OZ.Event.remove(this.documentUp); +} + +SQL.Map.prototype.sync = function() { /* when window changes, adjust map */ + var dims = OZ.DOM.win(); + var scroll = OZ.DOM.scroll(); + var scaleX = this.width / this.owner.width; + var scaleY = this.height / this.owner.height; + + var w = dims[0] * scaleX - 4 - 0; + var h = dims[1] * scaleY - 4 - 0; + var x = scroll[0] * scaleX; + var y = scroll[1] * scaleY; + + this.w = Math.round(w); + this.h = Math.round(h); + this.l = Math.round(x); + this.t = Math.round(y); + + this.redraw(); +} + +SQL.Map.prototype.redraw = function() { + this.dom.port.style.width = this.w+"px"; + this.dom.port.style.height = this.h+"px"; + this.dom.port.style.left = this.l+"px"; + this.dom.port.style.top = this.t+"px"; +} diff --git a/js/options.js b/js/options.js new file mode 100644 index 0000000..64dbd95 --- /dev/null +++ b/js/options.js @@ -0,0 +1,107 @@ +/* --------------------- options ------------ */ + +SQL.Options = OZ.Class(); + +SQL.Options.prototype.init = function(owner) { + this.owner = owner; + this.dom = { + container:OZ.$("opts"), + btn:OZ.$("options") + } + this.dom.btn.value = _("options"); + this.save = this.bind(this.save); + this.build(); +} + +SQL.Options.prototype.build = function() { + this.dom.optionlocale = OZ.$("optionlocale"); + this.dom.optiondb = OZ.$("optiondb"); + this.dom.optionsnap = OZ.$("optionsnap"); + this.dom.optionpattern = OZ.$("optionpattern"); + this.dom.optionhide = OZ.$("optionhide"); + this.dom.optionvector = OZ.$("optionvector"); + this.dom.optionshowsize = OZ.$("optionshowsize"); + this.dom.optionshowtype = OZ.$("optionshowtype"); + + var ids = ["language","db","snap","pattern","hide","vector","showsize","showtype","optionsnapnotice","optionpatternnotice","optionsnotice"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + elm.innerHTML = _(id); + } + + var ls = CONFIG.AVAILABLE_LOCALES; + OZ.DOM.clear(this.dom.optionlocale); + for (var i=0;i<ls.length;i++) { + var o = OZ.DOM.elm("option"); + o.value = ls[i]; + o.innerHTML = ls[i]; + this.dom.optionlocale.appendChild(o); + if (this.owner.getOption("locale") == ls[i]) { this.dom.optionlocale.selectedIndex = i; } + } + + var dbs = CONFIG.AVAILABLE_DBS; + OZ.DOM.clear(this.dom.optiondb); + for (var i=0;i<dbs.length;i++) { + var o = OZ.DOM.elm("option"); + o.value = dbs[i]; + o.innerHTML = dbs[i]; + this.dom.optiondb.appendChild(o); + if (this.owner.getOption("db") == dbs[i]) { this.dom.optiondb.selectedIndex = i; } + } + + + OZ.Event.add(this.dom.btn, "click", this.bind(this.click)); + + this.dom.container.parentNode.removeChild(this.dom.container); +} + +SQL.Options.prototype.save = function() { + this.owner.setOption("locale",this.dom.optionlocale.value); + this.owner.setOption("db",this.dom.optiondb.value); + this.owner.setOption("snap",this.dom.optionsnap.value); + this.owner.setOption("pattern",this.dom.optionpattern.value); + this.owner.setOption("hide",this.dom.optionhide.checked ? "1" : ""); + this.owner.setOption("vector",this.dom.optionvector.checked ? "1" : ""); + this.owner.setOption("showsize",this.dom.optionshowsize.checked ? "1" : ""); + this.owner.setOption("showtype",this.dom.optionshowtype.checked ? "1" : ""); +} + +SQL.Options.prototype.click = function() { + this.owner.window.open(_("options"),this.dom.container,this.save); + this.dom.optionsnap.value = this.owner.getOption("snap"); + this.dom.optionpattern.value = this.owner.getOption("pattern"); + this.dom.optionhide.checked = this.owner.getOption("hide"); + this.dom.optionvector.checked = this.owner.getOption("vector"); + this.dom.optionshowsize.checked = this.owner.getOption("showsize"); + this.dom.optionshowtype.checked = this.owner.getOption("showtype"); +} + +/* ------------------ minimize/restore bar ----------- */ + +SQL.Toggle = OZ.Class(); + +SQL.Toggle.prototype.init = function(elm) { + this._state = null; + this._elm = elm; + OZ.Event.add(elm, "click", this._click.bind(this)); + + var defaultState = true; + if (document.location.href.match(/toolbar=hidden/)) { defaultState = false; } + this._switch(defaultState); +} + +SQL.Toggle.prototype._click = function(e) { + this._switch(!this._state); +} + +SQL.Toggle.prototype._switch = function(state) { + this._state = state; + if (this._state) { + OZ.$("bar").style.height = ""; + } else { + OZ.$("bar").style.overflow = "hidden"; + OZ.$("bar").style.height = this._elm.offsetHeight + "px"; + } + this._elm.className = (this._state ? "on" : "off"); +} diff --git a/js/relation.js b/js/relation.js new file mode 100644 index 0000000..40e48c8 --- /dev/null +++ b/js/relation.js @@ -0,0 +1,158 @@ +/* --------------------------- relation (connector) ----------- */ + +SQL.Relation = OZ.Class().extend(SQL.Visual); +SQL.Relation._counter = 0; +SQL.Relation.prototype.init = function(owner, row1, row2) { + this.owner = owner; + this.row1 = row1; + this.row2 = row2; + this.color = "#000"; + this.hidden = false; + SQL.Visual.prototype.init.apply(this); + + /* if one of the rows already has relations, inherit color */ + var all = row1.relations.concat(row2.relations); + if (all.length) { /* inherit */ + this.color = all[0].getColor(); + } else if (CONFIG.RELATION_COLORS) { /* pick next */ + this.constructor._counter++; + var colorIndex = this.constructor._counter - 1; + this.color = CONFIG.RELATION_COLORS[colorIndex % CONFIG.RELATION_COLORS.length]; + } + + this.row1.addRelation(this); + this.row2.addRelation(this); + this.dom = []; + + if (this.owner.vector) { + var path = document.createElementNS(this.owner.svgNS, "path"); + path.setAttribute("stroke", this.color); + path.setAttribute("stroke-width", CONFIG.RELATION_THICKNESS); + path.setAttribute("fill", "none"); + this.owner.dom.svg.appendChild(path); + this.dom.push(path); + } else { + for (var i=0;i<3;i++) { + var div = OZ.DOM.elm("div",{position:"absolute",className:"relation",backgroundColor:this.color}); + this.dom.push(div); + if (i & 1) { /* middle */ + OZ.Style.set(div, {width:CONFIG.RELATION_THICKNESS+"px"}); + } else { /* first & last */ + OZ.Style.set(div, {height:CONFIG.RELATION_THICKNESS+"px"}); + } + this.owner.dom.container.appendChild(div); + } + } + + this.redraw(); +} + +SQL.Relation.prototype.getColor = function() { + return this.color; +} + +SQL.Relation.prototype.show = function() { + this.hidden = false; + for (var i=0;i<this.dom.length;i++) { + this.dom[i].style.visibility = ""; + } +} + +SQL.Relation.prototype.hide = function() { + this.hidden = true; + for (var i=0;i<this.dom.length;i++) { + this.dom[i].style.visibility = "hidden"; + } +} + +SQL.Relation.prototype.redrawNormal = function(p1, p2, half) { + if (this.owner.vector) { + var str = "M "+p1[0]+" "+p1[1]+" C "+(p1[0] + half)+" "+p1[1]+" "; + str += (p2[0]-half)+" "+p2[1]+" "+p2[0]+" "+p2[1]; + this.dom[0].setAttribute("d",str); + } else { + this.dom[0].style.left = p1[0]+"px"; + this.dom[0].style.top = p1[1]+"px"; + this.dom[0].style.width = half+"px"; + + this.dom[1].style.left = (p1[0] + half) + "px"; + this.dom[1].style.top = Math.min(p1[1],p2[1]) + "px"; + this.dom[1].style.height = (Math.abs(p1[1] - p2[1])+CONFIG.RELATION_THICKNESS)+"px"; + + this.dom[2].style.left = (p1[0]+half+1)+"px"; + this.dom[2].style.top = p2[1]+"px"; + this.dom[2].style.width = half+"px"; + } +} + +SQL.Relation.prototype.redrawSide = function(p1, p2, x) { + if (this.owner.vector) { + var str = "M "+p1[0]+" "+p1[1]+" C "+x+" "+p1[1]+" "; + str += x+" "+p2[1]+" "+p2[0]+" "+p2[1]; + this.dom[0].setAttribute("d",str); + } else { + this.dom[0].style.left = Math.min(x,p1[0])+"px"; + this.dom[0].style.top = p1[1]+"px"; + this.dom[0].style.width = Math.abs(p1[0]-x)+"px"; + + this.dom[1].style.left = x+"px"; + this.dom[1].style.top = Math.min(p1[1],p2[1]) + "px"; + this.dom[1].style.height = (Math.abs(p1[1] - p2[1])+CONFIG.RELATION_THICKNESS)+"px"; + + this.dom[2].style.left = Math.min(x,p2[0])+"px"; + this.dom[2].style.top = p2[1]+"px"; + this.dom[2].style.width = Math.abs(p2[0]-x)+"px"; + } +} + +SQL.Relation.prototype.redraw = function() { /* draw connector */ + if (this.hidden) { return; } + var t1 = this.row1.owner.dom.container; + var t2 = this.row2.owner.dom.container; + + var l1 = t1.offsetLeft; + var l2 = t2.offsetLeft; + var r1 = l1 + t1.offsetWidth; + var r2 = l2 + t2.offsetWidth; + var t1 = t1.offsetTop + this.row1.dom.container.offsetTop + Math.round(this.row1.dom.container.offsetHeight/2); + var t2 = t2.offsetTop + this.row2.dom.container.offsetTop + Math.round(this.row2.dom.container.offsetHeight/2); + + if (this.row1.owner.selected) { t1++; l1++; r1--; } + if (this.row2.owner.selected) { t2++; l2++; r2--; } + + var p1 = [0,0]; + var p2 = [0,0]; + + if (r1 < l2 || r2 < l1) { /* between tables */ + if (Math.abs(r1 - l2) < Math.abs(r2 - l1)) { + p1 = [r1,t1]; + p2 = [l2,t2]; + } else { + p1 = [r2,t2]; + p2 = [l1,t1]; + } + var half = Math.floor((p2[0] - p1[0])/2); + this.redrawNormal(p1, p2, half); + } else { /* next to tables */ + var x = 0; + var l = 0; + if (Math.abs(l1 - l2) < Math.abs(r1 - r2)) { /* left of tables */ + p1 = [l1,t1]; + p2 = [l2,t2]; + x = Math.min(l1,l2) - CONFIG.RELATION_SPACING; + } else { /* right of tables */ + p1 = [r1,t1]; + p2 = [r2,t2]; + x = Math.max(r1,r2) + CONFIG.RELATION_SPACING; + } + this.redrawSide(p1, p2, x); + } /* line next to tables */ +} + +SQL.Relation.prototype.destroy = function() { + this.row1.removeRelation(this); + this.row2.removeRelation(this); + for (var i=0;i<this.dom.length;i++) { + this.dom[i].parentNode.removeChild(this.dom[i]); + } +} diff --git a/js/row.js b/js/row.js new file mode 100644 index 0000000..e09afd5 --- /dev/null +++ b/js/row.js @@ -0,0 +1,425 @@ +/* --------------------- table row ( = db column) ------------ */ + +SQL.Row = OZ.Class().extend(SQL.Visual); + +SQL.Row.prototype.init = function(owner, title, data) { + this.owner = owner; + this.relations = []; + this.keys = []; + this.selected = false; + this.expanded = false; + + SQL.Visual.prototype.init.apply(this); + + this.data.type = 0; + this.data.size = ""; + this.data.def = null; + this.data.nll = true; + this.data.ai = false; + this.data.comment = ""; + + if (data) { this.update(data); } + this.setTitle(title); +} + +SQL.Row.prototype._build = function() { + this.dom.container = OZ.DOM.elm("tbody"); + + this.dom.content = OZ.DOM.elm("tr"); + this.dom.selected = OZ.DOM.elm("div", {className:"selected",innerHTML:"» "}); + this.dom.title = OZ.DOM.elm("div", {className:"title"}); + var td1 = OZ.DOM.elm("td"); + var td2 = OZ.DOM.elm("td", {className:"typehint"}); + this.dom.typehint = td2; + + OZ.DOM.append( + [this.dom.container, this.dom.content], + [this.dom.content, td1, td2], + [td1, this.dom.selected, this.dom.title] + ); + + this.enter = this.bind(this.enter); + this.changeComment = this.bind(this.changeComment); + + OZ.Event.add(this.dom.container, "click",this.bind(this.click)); + OZ.Event.add(this.dom.container, "dblclick",this.bind(this.dblclick)); +} + +SQL.Row.prototype.select = function() { + if (this.selected) { return; } + this.selected = true; + this.redraw(); +} + +SQL.Row.prototype.deselect = function() { + if (!this.selected) { return; } + this.selected = false; + this.redraw(); + this.collapse(); +} + +SQL.Row.prototype.setTitle = function(t) { + var old = this.getTitle(); + for (var i=0;i<this.relations.length;i++) { + var r = this.relations[i]; + if (r.row1 != this) { continue; } + var tt = r.row2.getTitle().replace(new RegExp(old,"g"),t); + if (tt != r.row2.getTitle()) { r.row2.setTitle(tt); } + } + + SQL.Visual.prototype.setTitle.apply(this, [t]); +} + +SQL.Row.prototype.click = function(e) { /* clicked on row */ + this.dispatch("rowclick", this); + this.owner.owner.rowManager.select(this); +} + +SQL.Row.prototype.dblclick = function(e) { /* dblclicked on row */ + OZ.Event.prevent(e); + OZ.Event.stop(e); + this.expand(); +} + +SQL.Row.prototype.update = function(data) { /* update subset of row data */ + var des = SQL.Designer; + if (data.nll && data.def && data.def.match(/^null$/i)) { data.def = null; } + + for (var p in data) { this.data[p] = data[p]; } + if (!this.data.nll && this.data.def === null) { this.data.def = ""; } + + var elm = this.getDataType(); + for (var i=0;i<this.relations.length;i++) { + var r = this.relations[i]; + if (r.row1 == this) { r.row2.update({type:des.getFKTypeFor(this.data.type),size:this.data.size}); } + } + this.redraw(); +} + +SQL.Row.prototype.up = function() { /* shift up */ + var r = this.owner.rows; + var idx = r.indexOf(this); + if (!idx) { return; } + r[idx-1].dom.container.parentNode.insertBefore(this.dom.container,r[idx-1].dom.container); + r.splice(idx,1); + r.splice(idx-1,0,this); + this.redraw(); +} + +SQL.Row.prototype.down = function() { /* shift down */ + var r = this.owner.rows; + var idx = r.indexOf(this); + if (idx+1 == this.owner.rows.length) { return; } + r[idx].dom.container.parentNode.insertBefore(this.dom.container,r[idx+1].dom.container.nextSibling); + r.splice(idx,1); + r.splice(idx+1,0,this); + this.redraw(); +} + +SQL.Row.prototype.buildEdit = function() { + OZ.DOM.clear(this.dom.container); + + var elms = []; + this.dom.name = OZ.DOM.elm("input"); + this.dom.name.type = "text"; + elms.push(["name",this.dom.name]); + OZ.Event.add(this.dom.name, "keypress", this.enter); + + this.dom.type = this.buildTypeSelect(this.data.type); + elms.push(["type",this.dom.type]); + + this.dom.size = OZ.DOM.elm("input"); + this.dom.size.type = "text"; + elms.push(["size",this.dom.size]); + + this.dom.def = OZ.DOM.elm("input"); + this.dom.def.type = "text"; + elms.push(["def",this.dom.def]); + + this.dom.ai = OZ.DOM.elm("input"); + this.dom.ai.type = "checkbox"; + elms.push(["ai",this.dom.ai]); + + this.dom.nll = OZ.DOM.elm("input"); + this.dom.nll.type = "checkbox"; + elms.push(["null",this.dom.nll]); + + this.dom.comment = OZ.DOM.elm("span",{className:"comment"}); + this.dom.comment.innerHTML = this.data.comment; + + this.dom.commentbtn = OZ.DOM.elm("input"); + this.dom.commentbtn.type = "button"; + this.dom.commentbtn.value = _("comment"); + + OZ.Event.add(this.dom.commentbtn, "click", this.changeComment); + + for (var i=0;i<elms.length;i++) { + var row = elms[i]; + var tr = OZ.DOM.elm("tr"); + var td1 = OZ.DOM.elm("td"); + var td2 = OZ.DOM.elm("td"); + var l = OZ.DOM.text(_(row[0])+": "); + OZ.DOM.append( + [tr, td1, td2], + [td1, l], + [td2, row[1]] + ); + this.dom.container.appendChild(tr); + } + + var tr = OZ.DOM.elm("tr"); + var td1 = OZ.DOM.elm("td"); + var td2 = OZ.DOM.elm("td"); + OZ.DOM.append( + [tr, td1, td2], + [td1, this.dom.comment], + [td2, this.dom.commentbtn] + ); + this.dom.container.appendChild(tr); +} + +SQL.Row.prototype.changeComment = function(e) { + var c = prompt(_("commenttext"),this.data.comment); + if (c === null) { return; } + this.data.comment = c; + this.dom.comment.innerHTML = this.data.comment; +} + +SQL.Row.prototype.expand = function() { + if (this.expanded) { return; } + this.expanded = true; + this.buildEdit(); + this.load(); + this.redraw(); + this.dom.name.focus(); + this.dom.name.select(); +} + +SQL.Row.prototype.collapse = function() { + if (!this.expanded) { return; } + this.expanded = false; + + var data = { + type: this.dom.type.selectedIndex, + def: this.dom.def.value, + size: this.dom.size.value, + nll: this.dom.nll.checked, + ai: this.dom.ai.checked + } + + OZ.DOM.clear(this.dom.container); + this.dom.container.appendChild(this.dom.content); + + this.update(data); + this.setTitle(this.dom.name.value); +} + +SQL.Row.prototype.load = function() { /* put data to expanded form */ + this.dom.name.value = this.getTitle(); + var def = this.data.def; + if (def === null) { def = "NULL"; } + + this.dom.def.value = def; + this.dom.size.value = this.data.size; + this.dom.nll.checked = this.data.nll; + this.dom.ai.checked = this.data.ai; +} + +SQL.Row.prototype.redraw = function() { + var color = this.getColor(); + this.dom.container.style.backgroundColor = color; + OZ.DOM.removeClass(this.dom.title, "primary"); + OZ.DOM.removeClass(this.dom.title, "key"); + if (this.isPrimary()) { OZ.DOM.addClass(this.dom.title, "primary"); } + if (this.isKey()) { OZ.DOM.addClass(this.dom.title, "key"); } + this.dom.selected.style.display = (this.selected ? "" : "none"); + this.dom.container.title = this.data.comment; + + var typehint = []; + if (this.owner.owner.getOption("showtype")) { + var elm = this.getDataType(); + typehint.push(elm.getAttribute("sql")); + } + + if (this.owner.owner.getOption("showsize") && this.data.size) { + typehint.push("(" + this.data.size + ")"); + } + + this.dom.typehint.innerHTML = typehint.join(" "); + this.owner.redraw(); + this.owner.owner.rowManager.redraw(); +} + +SQL.Row.prototype.addRelation = function(r) { + this.relations.push(r); +} + +SQL.Row.prototype.removeRelation = function(r) { + var idx = this.relations.indexOf(r); + if (idx == -1) { return; } + this.relations.splice(idx,1); +} + +SQL.Row.prototype.addKey = function(k) { + this.keys.push(k); + this.redraw(); +} + +SQL.Row.prototype.removeKey = function(k) { + var idx = this.keys.indexOf(k); + if (idx == -1) { return; } + this.keys.splice(idx,1); + this.redraw(); +} + +SQL.Row.prototype.getDataType = function() { + var type = this.data.type; + var elm = DATATYPES.getElementsByTagName("type")[type]; + return elm; +} + +SQL.Row.prototype.getColor = function() { + var elm = this.getDataType(); + var g = this.getDataType().parentNode; + return elm.getAttribute("color") || g.getAttribute("color") || "#fff"; +} + +SQL.Row.prototype.buildTypeSelect = function(id) { /* build selectbox with avail datatypes */ + var s = OZ.DOM.elm("select"); + var gs = DATATYPES.getElementsByTagName("group"); + for (var i=0;i<gs.length;i++) { + var g = gs[i]; + var og = OZ.DOM.elm("optgroup"); + og.style.backgroundColor = g.getAttribute("color") || "#fff"; + og.label = g.getAttribute("label"); + s.appendChild(og); + var ts = g.getElementsByTagName("type"); + for (var j=0;j<ts.length;j++) { + var t = ts[j]; + var o = OZ.DOM.elm("option"); + if (t.getAttribute("color")) { o.style.backgroundColor = t.getAttribute("color"); } + if (t.getAttribute("note")) { o.title = t.getAttribute("note"); } + o.innerHTML = t.getAttribute("label"); + og.appendChild(o); + } + } + s.selectedIndex = id; + return s; +} + +SQL.Row.prototype.destroy = function() { + SQL.Visual.prototype.destroy.apply(this); + while (this.relations.length) { + this.owner.owner.removeRelation(this.relations[0]); + } + for (var i=0;i<this.keys.length;i++){ + this.keys[i].removeRow(this); + } +} + +SQL.Row.prototype.toXML = function() { + var xml = ""; + + var t = this.getTitle().replace(/"/g,"""); + var nn = (this.data.nll ? "1" : "0"); + var ai = (this.data.ai ? "1" : "0"); + xml += '<row name="'+t+'" null="'+nn+'" autoincrement="'+ai+'">\n'; + + var elm = this.getDataType(); + var t = elm.getAttribute("sql"); + if (this.data.size.length) { t += "("+this.data.size+")"; } + xml += "<datatype>"+t+"</datatype>\n"; + + if (this.data.def || this.data.def === null) { + var q = elm.getAttribute("quote"); + var d = this.data.def; + if (d === null) { + d = "NULL"; + } else if (d != "CURRENT_TIMESTAMP") { + d = q+d+q; + } + xml += "<default>"+d+"</default>"; + } + + for (var i=0;i<this.relations.length;i++) { + var r = this.relations[i]; + if (r.row2 != this) { continue; } + xml += '<relation table="'+r.row1.owner.getTitle()+'" row="'+r.row1.getTitle()+'" />\n'; + } + + if (this.data.comment) { + var escaped = this.data.comment.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<"); + xml += "<comment>"+escaped+"</comment>\n"; + } + + xml += "</row>\n"; + return xml; +} + +SQL.Row.prototype.fromXML = function(node) { + var name = node.getAttribute("name"); + + var obj = { type:0, size:"" }; + obj.nll = (node.getAttribute("null") == "1"); + obj.ai = (node.getAttribute("autoincrement") == "1"); + + var cs = node.getElementsByTagName("comment"); + if (cs.length && cs[0].firstChild) { obj.comment = cs[0].firstChild.nodeValue; } + + var d = node.getElementsByTagName("datatype"); + if (d.length && d[0].firstChild) { + var s = d[0].firstChild.nodeValue; + var r = s.match(/^([^\(]+)(\((.*)\))?.*$/); + var type = r[1]; + if (r[3]) { obj.size = r[3]; } + var types = window.DATATYPES.getElementsByTagName("type"); + for (var i=0;i<types.length;i++) { + var sql = types[i].getAttribute("sql"); + var re = types[i].getAttribute("re"); + if (sql == type || (re && new RegExp(re).exec(type)) ) { obj.type = i; } + } + } + + var elm = DATATYPES.getElementsByTagName("type")[obj.type]; + var d = node.getElementsByTagName("default"); + if (d.length && d[0].firstChild) { + var def = d[0].firstChild.nodeValue; + obj.def = def; + var q = elm.getAttribute("quote"); + if (q) { + var re = new RegExp("^"+q+"(.*)"+q+"$"); + var r = def.match(re); + if (r) { obj.def = r[1]; } + } + } + + this.update(obj); + this.setTitle(name); +} + +SQL.Row.prototype.isPrimary = function() { + for (var i=0;i<this.keys.length;i++) { + var k = this.keys[i]; + if (k.getType() == "PRIMARY") { return true; } + } + return false; +} + +SQL.Row.prototype.isUnique = function() { + for (var i=0;i<this.keys.length;i++) { + var k = this.keys[i]; + var t = k.getType(); + if (t == "PRIMARY" || t == "UNIQUE") { return true; } + } + return false; +} + +SQL.Row.prototype.isKey = function() { + return this.keys.length > 0; +} + +SQL.Row.prototype.enter = function(e) { + if (e.keyCode == 13) { + this.collapse(); + } +} diff --git a/js/rowmanager.js b/js/rowmanager.js new file mode 100644 index 0000000..a0257f6 --- /dev/null +++ b/js/rowmanager.js @@ -0,0 +1,190 @@ +/* --------------------- row manager ------------ */ + +SQL.RowManager = OZ.Class(); + +SQL.RowManager.prototype.init = function(owner) { + this.owner = owner; + this.dom = {}; + this.selected = null; + this.creating = false; + this.connecting = false; + + var ids = ["editrow","removerow","uprow","downrow","foreigncreate","foreignconnect","foreigndisconnect"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + this.dom[id] = elm; + elm.value = _(id); + } + + this.select(false); + + OZ.Event.add(this.dom.editrow, "click", this.bind(this.edit)); + OZ.Event.add(this.dom.uprow, "click", this.bind(this.up)); + OZ.Event.add(this.dom.downrow, "click", this.bind(this.down)); + OZ.Event.add(this.dom.removerow, "click", this.bind(this.remove)); + OZ.Event.add(this.dom.foreigncreate, "click", this.bind(this.foreigncreate)); + OZ.Event.add(this.dom.foreignconnect, "click", this.bind(this.foreignconnect)); + OZ.Event.add(this.dom.foreigndisconnect, "click", this.bind(this.foreigndisconnect)); + OZ.Event.add(false, "tableclick", this.bind(this.tableClick)); + OZ.Event.add(false, "rowclick", this.bind(this.rowClick)); + OZ.Event.add(document, "keydown", this.bind(this.press)); +} + +SQL.RowManager.prototype.select = function(row) { /* activate a row */ + if (this.selected === row) { return; } + if (this.selected) { this.selected.deselect(); } + + this.selected = row; + if (this.selected) { this.selected.select(); } + this.redraw(); +} + +SQL.RowManager.prototype.tableClick = function(e) { /* create relation after clicking target table */ + if (!this.creating) { return; } + + var r1 = this.selected; + var t2 = e.target; + + var p = this.owner.getOption("pattern"); + p = p.replace(/%T/g,r1.owner.getTitle()); + p = p.replace(/%t/g,t2.getTitle()); + p = p.replace(/%R/g,r1.getTitle()); + + var r2 = t2.addRow(p, r1.data); + r2.update({"type":SQL.Designer.getFKTypeFor(r1.data.type)}); + r2.update({"ai":false}); + this.owner.addRelation(r1, r2); +} + +SQL.RowManager.prototype.rowClick = function(e) { /* draw relation after clicking target row */ + if (!this.connecting) { return; } + + var r1 = this.selected; + var r2 = e.target; + + if (r1 == r2) { return; } + + this.owner.addRelation(r1, r2); +} + +SQL.RowManager.prototype.foreigncreate = function(e) { /* start creating fk */ + this.endConnect(); + if (this.creating) { + this.endCreate(); + } else { + this.creating = true; + this.dom.foreigncreate.value = "["+_("foreignpending")+"]"; + } +} + +SQL.RowManager.prototype.foreignconnect = function(e) { /* start drawing fk */ + this.endCreate(); + if (this.connecting) { + this.endConnect(); + } else { + this.connecting = true; + this.dom.foreignconnect.value = "["+_("foreignconnectpending")+"]"; + } +} + +SQL.RowManager.prototype.foreigndisconnect = function(e) { /* remove connector */ + var rels = this.selected.relations; + for (var i=rels.length-1;i>=0;i--) { + var r = rels[i]; + if (r.row2 == this.selected) { this.owner.removeRelation(r); } + } + this.redraw(); +} + +SQL.RowManager.prototype.endCreate = function() { + this.creating = false; + this.dom.foreigncreate.value = _("foreigncreate"); +} + +SQL.RowManager.prototype.endConnect = function() { + this.connecting = false; + this.dom.foreignconnect.value = _("foreignconnect"); +} + +SQL.RowManager.prototype.up = function(e) { + this.selected.up(); + this.redraw(); +} + +SQL.RowManager.prototype.down = function(e) { + this.selected.down(); + this.redraw(); +} + +SQL.RowManager.prototype.remove = function(e) { + var result = confirm(_("confirmrow")+" '"+this.selected.getTitle()+"' ?"); + if (!result) { return; } + var t = this.selected.owner; + this.selected.owner.removeRow(this.selected); + + var next = false; + if (t.rows) { next = t.rows[t.rows.length-1]; } + this.select(next); +} + +SQL.RowManager.prototype.redraw = function() { + this.endCreate(); + this.endConnect(); + if (this.selected) { + var table = this.selected.owner; + var rows = table.rows; + this.dom.uprow.disabled = (rows[0] == this.selected); + this.dom.downrow.disabled = (rows[rows.length-1] == this.selected); + this.dom.removerow.disabled = false; + this.dom.editrow.disabled = false; + this.dom.foreigncreate.disabled = !(this.selected.isUnique()); + this.dom.foreignconnect.disabled = !(this.selected.isUnique()); + + this.dom.foreigndisconnect.disabled = true; + var rels = this.selected.relations; + for (var i=0;i<rels.length;i++) { + var r = rels[i]; + if (r.row2 == this.selected) { this.dom.foreigndisconnect.disabled = false; } + } + + } else { + this.dom.uprow.disabled = true; + this.dom.downrow.disabled = true; + this.dom.removerow.disabled = true; + this.dom.editrow.disabled = true; + this.dom.foreigncreate.disabled = true; + this.dom.foreignconnect.disabled = true; + this.dom.foreigndisconnect.disabled = true; + } +} + +SQL.RowManager.prototype.press = function(e) { + if (!this.selected) { return; } + + var target = OZ.Event.target(e).nodeName.toLowerCase(); + if (target == "textarea" || target == "input") { return; } /* not when in form field */ + + switch (e.keyCode) { + case 38: + this.up(); + OZ.Event.prevent(e); + break; + case 40: + this.down(); + OZ.Event.prevent(e); + break; + case 46: + this.remove(); + OZ.Event.prevent(e); + break; + case 13: + case 27: + this.selected.collapse(); + break; + } +} + +SQL.RowManager.prototype.edit = function(e) { + this.selected.expand(); +} diff --git a/js/rubberband.js b/js/rubberband.js new file mode 100644 index 0000000..1c2d0c2 --- /dev/null +++ b/js/rubberband.js @@ -0,0 +1,49 @@ +/* --------------------- rubberband -------------------- */ + +SQL.Rubberband = OZ.Class().extend(SQL.Visual); + +SQL.Rubberband.prototype.init = function(owner) { + this.owner = owner; + SQL.Visual.prototype.init.apply(this); + this.dom.container = OZ.$("rubberband"); + OZ.Event.add("area", "mousedown", this.bind(this.down)); +} + +SQL.Rubberband.prototype.down = function(e) { + OZ.Event.prevent(e); + var scroll = OZ.DOM.scroll(); + this.x = this.x0 = e.clientX + scroll[0]; + this.y = this.y0 = e.clientY + scroll[1]; + this.width = 0; + this.height = 0; + this.redraw(); + this.documentMove = OZ.Event.add(document, "mousemove", this.bind(this.move)); + this.documentUp = OZ.Event.add(document, "mouseup", this.bind(this.up)); +} + +SQL.Rubberband.prototype.move = function(e) { + var scroll = OZ.DOM.scroll(); + var x = e.clientX + scroll[0]; + var y = e.clientY + scroll[1]; + this.width = Math.abs(x-this.x0); + this.height = Math.abs(y-this.y0); + if (x<this.x0) { this.x = x; } else { this.x = this.x0; } + if (y<this.y0) { this.y = y; } else { this.y = this.y0; } + this.redraw(); + this.dom.container.style.visibility = "visible"; +} + +SQL.Rubberband.prototype.up = function(e) { + OZ.Event.prevent(e); + this.dom.container.style.visibility = "hidden"; + OZ.Event.remove(this.documentMove); + OZ.Event.remove(this.documentUp); + this.owner.tableManager.selectRect(this.x, this.y, this.width, this.height); +} + +SQL.Rubberband.prototype.redraw = function() { + this.dom.container.style.left = this.x+"px"; + this.dom.container.style.top = this.y+"px"; + this.dom.container.style.width = this.width+"px"; + this.dom.container.style.height = this.height+"px"; +} diff --git a/js/table.js b/js/table.js new file mode 100644 index 0000000..b9c53a0 --- /dev/null +++ b/js/table.js @@ -0,0 +1,362 @@ +/* --------------------- db table ------------ */ + +SQL.Table = OZ.Class().extend(SQL.Visual); + +SQL.Table.prototype.init = function(owner, name, x, y, z) { + this.owner = owner; + this.rows = []; + this.keys = []; + this.zIndex = 0; + this._ec = []; + + this.flag = false; + this.selected = false; + SQL.Visual.prototype.init.apply(this); + this.data.comment = ""; + + this.setTitle(name); + this.x = x || 0; + this.y = y || 0; + this.setZ(z); + this.snap(); +} + +SQL.Table.prototype._build = function() { + this.dom.container = OZ.DOM.elm("div", {className:"table"}); + this.dom.content = OZ.DOM.elm("table"); + var thead = OZ.DOM.elm("thead"); + var tr = OZ.DOM.elm("tr"); + this.dom.title = OZ.DOM.elm("td", {className:"title", colSpan:2}); + + OZ.DOM.append( + [this.dom.container, this.dom.content], + [this.dom.content, thead], + [thead, tr], + [tr, this.dom.title] + ); + + this.dom.mini = OZ.DOM.elm("div", {className:"mini"}); + this.owner.map.dom.container.appendChild(this.dom.mini); + + this._ec.push(OZ.Event.add(this.dom.container, "click", this.bind(this.click))); + this._ec.push(OZ.Event.add(this.dom.container, "dblclick", this.bind(this.dblclick))); + this._ec.push(OZ.Event.add(this.dom.container, "mousedown", this.bind(this.down))); + this._ec.push(OZ.Event.add(this.dom.container, "touchstart", this.bind(this.down))); + this._ec.push(OZ.Event.add(this.dom.container, "touchmove", OZ.Event.prevent)); +} + +SQL.Table.prototype.setTitle = function(t) { + var old = this.getTitle(); + for (var i=0;i<this.rows.length;i++) { + var row = this.rows[i]; + for (var j=0;j<row.relations.length;j++) { + var r = row.relations[j]; + if (r.row1 != row) { continue; } + var tt = row.getTitle().replace(new RegExp(old,"g"),t); + if (tt != row.getTitle()) { row.setTitle(tt); } + } + } + SQL.Visual.prototype.setTitle.apply(this, [t]); +} + +SQL.Table.prototype.getRelations = function() { + var arr = []; + for (var i=0;i<this.rows.length;i++) { + var row = this.rows[i]; + for (var j=0;j<row.relations.length;j++) { + var r = row.relations[j]; + if (arr.indexOf(r) == -1) { arr.push(r); } + } + } + return arr; +} + +SQL.Table.prototype.showRelations = function() { + var rs = this.getRelations(); + for (var i=0;i<rs.length;i++) { rs[i].show(); } +} + +SQL.Table.prototype.hideRelations = function() { + var rs = this.getRelations(); + for (var i=0;i<rs.length;i++) { rs[i].hide(); } +} + +SQL.Table.prototype.click = function(e) { + OZ.Event.stop(e); + var t = OZ.Event.target(e); + this.owner.tableManager.select(this); + + if (t != this.dom.title) { return; } /* click on row */ + + this.dispatch("tableclick",this); + this.owner.rowManager.select(false); +} + +SQL.Table.prototype.dblclick = function(e) { + var t = OZ.Event.target(e); + if (t == this.dom.title) { this.owner.tableManager.edit(); } +} + +SQL.Table.prototype.select = function() { + if (this.selected) { return; } + this.selected = true; + OZ.DOM.addClass(this.dom.container, "selected"); + OZ.DOM.addClass(this.dom.mini, "mini_selected"); + this.redraw(); +} + +SQL.Table.prototype.deselect = function() { + if (!this.selected) { return; } + this.selected = false; + OZ.DOM.removeClass(this.dom.container, "selected"); + OZ.DOM.removeClass(this.dom.mini, "mini_selected"); + this.redraw(); +} + +SQL.Table.prototype.addRow = function(title, data) { + var r = new SQL.Row(this, title, data); + this.rows.push(r); + this.dom.content.appendChild(r.dom.container); + this.redraw(); + return r; +} + +SQL.Table.prototype.removeRow = function(r) { + var idx = this.rows.indexOf(r); + if (idx == -1) { return; } + r.destroy(); + this.rows.splice(idx,1); + this.redraw(); +} + +SQL.Table.prototype.addKey = function(name) { + var k = new SQL.Key(this, name); + this.keys.push(k); + return k; +} + +SQL.Table.prototype.removeKey = function(i) { + var idx = this.keys.indexOf(k); + if (idx == -1) { return; } + k.destroy(); + this.keys.splice(idx,1); +} + +SQL.Table.prototype.redraw = function() { + var x = this.x; + var y = this.y; + if (this.selected) { x--; y--; } + this.dom.container.style.left = x+"px"; + this.dom.container.style.top = y+"px"; + + var ratioX = this.owner.map.width / this.owner.width; + var ratioY = this.owner.map.height / this.owner.height; + + var w = this.dom.container.offsetWidth * ratioX; + var h = this.dom.container.offsetHeight * ratioY; + var x = this.x * ratioX; + var y = this.y * ratioY; + + this.dom.mini.style.width = Math.round(w)+"px"; + this.dom.mini.style.height = Math.round(h)+"px"; + this.dom.mini.style.left = Math.round(x)+"px"; + this.dom.mini.style.top = Math.round(y)+"px"; + + this.width = this.dom.container.offsetWidth; + this.height = this.dom.container.offsetHeight; + + var rs = this.getRelations(); + for (var i=0;i<rs.length;i++) { rs[i].redraw(); } +} + +SQL.Table.prototype.moveBy = function(dx, dy) { + this.x += dx; + this.y += dy; + + this.snap(); + this.redraw(); +} + +SQL.Table.prototype.moveTo = function(x, y) { + this.x = x; + this.y = y; + + this.snap(); + this.redraw(); +} + +SQL.Table.prototype.snap = function() { + var snap = parseInt(SQL.Designer.getOption("snap")); + if (snap) { + this.x = Math.round(this.x / snap) * snap; + this.y = Math.round(this.y / snap) * snap; + } +} + +SQL.Table.prototype.down = function(e) { /* mousedown - start drag */ + OZ.Event.stop(e); + var t = OZ.Event.target(e); + if (t != this.dom.title) { return; } /* on a row */ + + /* touch? */ + if (e.type == "touchstart") { + var event = e.touches[0]; + var moveEvent = "touchmove"; + var upEvent = "touchend"; + } else { + var event = e; + var moveEvent = "mousemove"; + var upEvent = "mouseup"; + } + + /* a non-shift click within a selection preserves the selection */ + if (e.shiftKey || ! this.selected) { + this.owner.tableManager.select(this, e.shiftKey); + } + + var t = SQL.Table; + t.active = this.owner.tableManager.selection; + var n = t.active.length; + t.x = new Array(n); + t.y = new Array(n); + for (var i=0;i<n;i++) { + /* position relative to mouse cursor */ + t.x[i] = t.active[i].x - event.clientX; + t.y[i] = t.active[i].y - event.clientY; + } + + if (this.owner.getOption("hide")) { + for (var i=0;i<n;i++) { + t.active[i].hideRelations(); + } + } + + this.documentMove = OZ.Event.add(document, moveEvent, this.bind(this.move)); + this.documentUp = OZ.Event.add(document, upEvent, this.bind(this.up)); +} + +SQL.Table.prototype.toXML = function() { + var t = this.getTitle().replace(/"/g,"""); + var xml = ""; + xml += '<table x="'+this.x+'" y="'+this.y+'" name="'+t+'">\n'; + for (var i=0;i<this.rows.length;i++) { + xml += this.rows[i].toXML(); + } + for (var i=0;i<this.keys.length;i++) { + xml += this.keys[i].toXML(); + } + var c = this.getComment(); + if (c) { + c = c.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<"); + xml += "<comment>"+c+"</comment>\n"; + } + xml += "</table>\n"; + return xml; +} + +SQL.Table.prototype.fromXML = function(node) { + var name = node.getAttribute("name"); + this.setTitle(name); + var x = parseInt(node.getAttribute("x")) || 0; + var y = parseInt(node.getAttribute("y")) || 0; + this.moveTo(x, y); + var rows = node.getElementsByTagName("row"); + for (var i=0;i<rows.length;i++) { + var row = rows[i]; + var r = this.addRow(""); + r.fromXML(row); + } + var keys = node.getElementsByTagName("key"); + for (var i=0;i<keys.length;i++) { + var key = keys[i]; + var k = this.addKey(); + k.fromXML(key); + } + for (var i=0;i<node.childNodes.length;i++) { + var ch = node.childNodes[i]; + if (ch.tagName && ch.tagName.toLowerCase() == "comment" && ch.firstChild) { + this.setComment(ch.firstChild.nodeValue); + } + } +} + +SQL.Table.prototype.getZ = function() { + return this.zIndex; +} + +SQL.Table.prototype.setZ = function(z) { + this.zIndex = z; + this.dom.container.style.zIndex = z; +} + +SQL.Table.prototype.findNamedRow = function(n) { /* return row with a given name */ + for (var i=0;i<this.rows.length;i++) { + if (this.rows[i].getTitle() == n) { return this.rows[i]; } + } + return false; +} + +SQL.Table.prototype.addKey = function(type, name) { + var i = new SQL.Key(this, type, name); + this.keys.push(i); + return i; +} + +SQL.Table.prototype.removeKey = function(i) { + var idx = this.keys.indexOf(i); + if (idx == -1) { return; } + i.destroy(); + this.keys.splice(idx,1); +} + +SQL.Table.prototype.setComment = function(c) { + this.data.comment = c; + this.dom.title.title = this.data.comment; +} + +SQL.Table.prototype.getComment = function() { + return this.data.comment; +} + +SQL.Table.prototype.move = function(e) { /* mousemove */ + var t = SQL.Table; + SQL.Designer.removeSelection(); + if (e.type == "touchmove") { + if (e.touches.length > 1) { return; } + var event = e.touches[0]; + } else { + var event = e; + } + + for (var i=0;i<t.active.length;i++) { + var x = t.x[i] + event.clientX; + var y = t.y[i] + event.clientY; + x = Math.max(x, 0); + y = Math.max(y, 0); + t.active[i].moveTo(x,y); + } +} + +SQL.Table.prototype.up = function(e) { + var t = SQL.Table; + var d = SQL.Designer; + if (d.getOption("hide")) { + for (var i=0;i<t.active.length;i++) { + t.active[i].showRelations(); + t.active[i].redraw(); + } + } + t.active = false; + OZ.Event.remove(this.documentMove); + OZ.Event.remove(this.documentUp); + this.owner.sync(); +} + +SQL.Table.prototype.destroy = function() { + SQL.Visual.prototype.destroy.apply(this); + this.dom.mini.parentNode.removeChild(this.dom.mini); + while (this.rows.length) { + this.removeRow(this.rows[0]); + } + this._ec.forEach(OZ.Event.remove, OZ.Event); +} diff --git a/js/tablemanager.js b/js/tablemanager.js new file mode 100644 index 0000000..29f1b6a --- /dev/null +++ b/js/tablemanager.js @@ -0,0 +1,211 @@ +/* --------------------- table manager ------------ */ + +SQL.TableManager = OZ.Class(); + +SQL.TableManager.prototype.init = function(owner) { + this.owner = owner; + this.dom = { + container:OZ.$("table"), + name:OZ.$("tablename"), + comment:OZ.$("tablecomment") + }; + this.selection = []; + this.adding = false; + + var ids = ["addtable","removetable","aligntables","cleartables","addrow","edittable","tablekeys"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + this.dom[id] = elm; + elm.value = _(id); + } + + var ids = ["tablenamelabel","tablecommentlabel"]; + for (var i=0;i<ids.length;i++) { + var id = ids[i]; + var elm = OZ.$(id); + elm.innerHTML = _(id); + } + + + this.select(false); + + this.save = this.bind(this.save); + + OZ.Event.add("area", "click", this.bind(this.click)); + OZ.Event.add(this.dom.addtable, "click", this.bind(this.preAdd)); + OZ.Event.add(this.dom.removetable, "click", this.bind(this.remove)); + OZ.Event.add(this.dom.cleartables, "click", this.bind(this.clear)); + OZ.Event.add(this.dom.addrow, "click", this.bind(this.addRow)); + OZ.Event.add(this.dom.aligntables, "click", this.owner.bind(this.owner.alignTables)); + OZ.Event.add(this.dom.edittable, "click", this.bind(this.edit)); + OZ.Event.add(this.dom.tablekeys, "click", this.bind(this.keys)); + OZ.Event.add(document, "keydown", this.bind(this.press)); + + this.dom.container.parentNode.removeChild(this.dom.container); +} + +SQL.TableManager.prototype.addRow = function(e) { + var newrow = this.selection[0].addRow(_("newrow")); + this.owner.rowManager.select(newrow); + newrow.expand(); +} + +SQL.TableManager.prototype.select = function(table, multi) { /* activate table */ + if (table) { + if (multi) { + var i = this.selection.indexOf(table); + if (i < 0) { + this.selection.push(table); + } else { + this.selection.splice(i, 1); + } + } else { + if (this.selection[0] === table) { return; } + this.selection = [table]; + } + } else { + this.selection = []; + } + this.processSelection(); +} + +SQL.TableManager.prototype.processSelection = function() { + var tables = this.owner.tables; + for (var i=0;i<tables.length;i++) { + tables[i].deselect(); + } + if (this.selection.length == 1) { + this.dom.addrow.disabled = false; + this.dom.edittable.disabled = false; + this.dom.tablekeys.disabled = false; + this.dom.removetable.value = _("removetable"); + } else { + this.dom.addrow.disabled = true; + this.dom.edittable.disabled = true; + this.dom.tablekeys.disabled = true; + } + if (this.selection.length) { + this.dom.removetable.disabled = false; + if (this.selection.length > 1) { this.dom.removetable.value = _("removetables"); } + } else { + this.dom.removetable.disabled = true; + this.dom.removetable.value = _("removetable"); + } + for (var i=0;i<this.selection.length;i++) { + var t = this.selection[i]; + t.owner.raise(t); + t.select(); + } +} + +SQL.TableManager.prototype.selectRect = function(x,y,width,height) { /* select all tables intersecting a rectangle */ + this.selection = []; + var tables = this.owner.tables; + var x1 = x+width; + var y1 = y+height; + for (var i=0;i<tables.length;i++) { + var t = tables[i]; + var tx = t.x; + var tx1 = t.x+t.width; + var ty = t.y; + var ty1 = t.y+t.height; + if (((tx>=x && tx<x1) || (tx1>=x && tx1<x1) || (tx<x && tx1>x1)) && + ((ty>=y && ty<y1) || (ty1>=y && ty1<y1) || (ty<y && ty1>y1))) + { this.selection.push(t); } + } + this.processSelection(); +} + +SQL.TableManager.prototype.click = function(e) { /* finish adding new table */ + var newtable = false; + if (this.adding) { + this.adding = false; + OZ.DOM.removeClass("area","adding"); + this.dom.addtable.value = this.oldvalue; + var scroll = OZ.DOM.scroll(); + var x = e.clientX + scroll[0]; + var y = e.clientY + scroll[1]; + newtable = this.owner.addTable(_("newtable"),x,y); + var r = newtable.addRow("id",{ai:true}); + var k = newtable.addKey("PRIMARY",""); + k.addRow(r); + } + this.select(newtable); + this.owner.rowManager.select(false); + if (this.selection.length == 1) { this.edit(e); } +} + +SQL.TableManager.prototype.preAdd = function(e) { /* click add new table */ + if (this.adding) { + this.adding = false; + OZ.DOM.removeClass("area","adding"); + this.dom.addtable.value = this.oldvalue; + } else { + this.adding = true; + OZ.DOM.addClass("area","adding"); + this.oldvalue = this.dom.addtable.value; + this.dom.addtable.value = "["+_("addpending")+"]"; + } +} + +SQL.TableManager.prototype.clear = function(e) { /* remove all tables */ + if (!this.owner.tables.length) { return; } + var result = confirm(_("confirmall")+" ?"); + if (!result) { return; } + this.owner.clearTables(); +} + +SQL.TableManager.prototype.remove = function(e) { + var titles = this.selection.slice(0); + for (var i=0;i<titles.length;i++) { titles[i] = "'"+titles[i].getTitle()+"'"; } + var result = confirm(_("confirmtable")+" "+titles.join(", ")+"?"); + if (!result) { return; } + var sel = this.selection.slice(0); + for (var i=0;i<sel.length;i++) { this.owner.removeTable(sel[i]); } +} + +SQL.TableManager.prototype.edit = function(e) { + this.owner.window.open(_("edittable"), this.dom.container, this.save); + + var title = this.selection[0].getTitle(); + this.dom.name.value = title; + try { /* throws in ie6 */ + this.dom.comment.value = this.selection[0].getComment(); + } catch(e) {} + + /* pre-select table name */ + this.dom.name.focus(); + if (OZ.ie) { + try { /* throws in ie6 */ + this.dom.name.select(); + } catch(e) {} + } else { + this.dom.name.setSelectionRange(0, title.length); + } +} + +SQL.TableManager.prototype.keys = function(e) { /* open keys dialog */ + this.owner.keyManager.open(this.selection[0]); +} + +SQL.TableManager.prototype.save = function() { + this.selection[0].setTitle(this.dom.name.value); + this.selection[0].setComment(this.dom.comment.value); +} + +SQL.TableManager.prototype.press = function(e) { + var target = OZ.Event.target(e).nodeName.toLowerCase(); + if (target == "textarea" || target == "input") { return; } /* not when in form field */ + + if (this.owner.rowManager.selected) { return; } /* do not process keypresses if a row is selected */ + + if (!this.selection.length) { return; } /* nothing if selection is active */ + + switch (e.keyCode) { + case 46: + this.remove(); + OZ.Event.prevent(e); + break; + } +} diff --git a/js/visual.js b/js/visual.js new file mode 100644 index 0000000..4fb7045 --- /dev/null +++ b/js/visual.js @@ -0,0 +1,42 @@ +/* -------------------- base visual element -------------------- */ + +SQL.Visual = OZ.Class(); /* abstract parent */ +SQL.Visual.prototype.init = function() { + this._init(); + this._build(); +} + +SQL.Visual.prototype._init = function() { + this.dom = { + container: null, + title: null + }; + this.data = { + title:"" + } +} + +SQL.Visual.prototype._build = function() {} + +SQL.Visual.prototype.toXML = function() {} + +SQL.Visual.prototype.fromXML = function(node) {} + +SQL.Visual.prototype.destroy = function() { /* "destructor" */ + var p = this.dom.container.parentNode; + if (p && p.nodeType == 1) { + p.removeChild(this.dom.container); + } +} + +SQL.Visual.prototype.setTitle = function(text) { + if (!text) { return; } + this.data.title = text; + this.dom.title.innerHTML = text; +} + +SQL.Visual.prototype.getTitle = function() { + return this.data.title; +} + +SQL.Visual.prototype.redraw = function() {} diff --git a/js/window.js b/js/window.js new file mode 100644 index 0000000..fd25bd6 --- /dev/null +++ b/js/window.js @@ -0,0 +1,95 @@ +/* --------------------- window ------------ */ + +SQL.Window = OZ.Class(); + +SQL.Window.prototype.init = function(owner) { + this.owner = owner; + this.dom = { + container:OZ.$("window"), + background:OZ.$("background"), + ok:OZ.$("windowok"), + cancel:OZ.$("windowcancel"), + title:OZ.$("windowtitle"), + content:OZ.$("windowcontent"), + throbber:OZ.$("throbber") + } + this.dom.ok.value = _("windowok"); + this.dom.cancel.value = _("windowcancel"); + this.dom.throbber.alt = this.dom.throbber.title = _("throbber"); + OZ.Event.add(this.dom.ok, "click", this.bind(this.ok)); + OZ.Event.add(this.dom.cancel, "click", this.bind(this.close)); + OZ.Event.add(document, "keydown", this.bind(this.key)); + + this.sync = this.bind(this.sync); + + OZ.Event.add(window, "scroll", this.sync); + OZ.Event.add(window, "resize", this.sync); + this.state = 0; + this.hideThrobber(); + + this.sync(); +} + +SQL.Window.prototype.showThrobber = function() { + this.dom.throbber.style.visibility = ""; +} + +SQL.Window.prototype.hideThrobber = function() { + this.dom.throbber.style.visibility = "hidden"; +} + +SQL.Window.prototype.open = function(title, content, callback) { + this.state = 1; + this.callback = callback; + while (this.dom.title.childNodes.length > 1) { this.dom.title.removeChild(this.dom.title.childNodes[1]); } + + var txt = OZ.DOM.text(title); + this.dom.title.appendChild(txt); + this.dom.background.style.visibility = "visible"; + OZ.DOM.clear(this.dom.content); + this.dom.content.appendChild(content); + + var win = OZ.DOM.win(); + var scroll = OZ.DOM.scroll(); + this.dom.container.style.left = Math.round(scroll[0] + (win[0] - this.dom.container.offsetWidth)/2)+"px"; + this.dom.container.style.top = Math.round(scroll[1] + (win[1] - this.dom.container.offsetHeight)/2)+"px"; + + this.dom.cancel.style.visibility = (this.callback ? "" : "hidden"); + this.dom.container.style.visibility = "visible"; + + var formElements = ["input","select","textarea"]; + var all = this.dom.container.getElementsByTagName("*"); + for (var i=0;i<all.length;i++) { + if (formElements.indexOf(all[i].tagName.toLowerCase()) != -1) { + all[i].focus(); + break; + } + } +} + +SQL.Window.prototype.key = function(e) { + if (!this.state) { return; } + if (e.keyCode == 13) { this.ok(e); } + if (e.keyCode == 27) { this.close(); } +} + +SQL.Window.prototype.ok = function(e) { + if (this.callback) { this.callback(); } + this.close(); +} + +SQL.Window.prototype.close = function() { + if (!this.state) { return; } + this.state = 0; + this.dom.background.style.visibility = "hidden"; + this.dom.container.style.visibility = "hidden"; +} + +SQL.Window.prototype.sync = function() { /* adjust background position */ + var dims = OZ.DOM.win(); + var scroll = OZ.DOM.scroll(); + this.dom.background.style.width = dims[0]+"px"; + this.dom.background.style.height = dims[1]+"px"; + this.dom.background.style.left = scroll[0]+"px"; + this.dom.background.style.top = scroll[1]+"px"; +} diff --git a/js/wwwsqldesigner.js b/js/wwwsqldesigner.js index 235e1ad..d1bd0a4 100755 --- a/js/wwwsqldesigner.js +++ b/js/wwwsqldesigner.js @@ -1,2579 +1,3 @@ -/* -------------------- configuration -------------------- */ - -/* - * The key below needs to be set individually by you if you want to use the Dropbox load/save feature. - * To do that, first sign up with Dropbox (may require a specific developer / SDK sign-up), go to - * https://www.dropbox.com/developers/apps and use "Create app" to add a new app. Call it, for instance, - * "wwwsqldesigner", and give it the "App Folder" permission. Unter "OAuth 2", "Redirect URIs", add - * the URL to the "dropbox-oauth-receiver.html" file on your server. E.g, if you install wwwsqldesigner - * on your local web server under "http://localhost/sqldesigner/", then add - * http://localhost/sqldesigner/dropbox-oauth-receiver.html as a Redirection URI. - * Copy the shown "App key" and paste it here below: - */ -var dropboxAppKey = null; // "your app key"; - - -/* -------------------- globals -------------------- */ - -function _(str) { /* getText */ - if (!(str in window.LOCALE)) { return str; } - return window.LOCALE[str]; -} - -if (typeof String.prototype.endsWith !== 'function') { - String.prototype.endsWith = function(suffix) { - return this.indexOf(suffix, this.length - suffix.length) !== -1; - }; -} - -var DATATYPES = false; -var LOCALE = {}; -var SQL = {}; - -/* -------------------- base visual element -------------------- */ - -SQL.Visual = OZ.Class(); /* abstract parent */ -SQL.Visual.prototype.init = function() { - this._init(); - this._build(); -} - -SQL.Visual.prototype._init = function() { - this.dom = { - container: null, - title: null - }; - this.data = { - title:"" - } -} - -SQL.Visual.prototype._build = function() {} - -SQL.Visual.prototype.toXML = function() {} - -SQL.Visual.prototype.fromXML = function(node) {} - -SQL.Visual.prototype.destroy = function() { /* "destructor" */ - var p = this.dom.container.parentNode; - if (p && p.nodeType == 1) { - p.removeChild(this.dom.container); - } -} - -SQL.Visual.prototype.setTitle = function(text) { - if (!text) { return; } - this.data.title = text; - this.dom.title.innerHTML = text; -} - -SQL.Visual.prototype.getTitle = function() { - return this.data.title; -} - -SQL.Visual.prototype.redraw = function() {} - -/* --------------------- table row ( = db column) ------------ */ - -SQL.Row = OZ.Class().extend(SQL.Visual); - -SQL.Row.prototype.init = function(owner, title, data) { - this.owner = owner; - this.relations = []; - this.keys = []; - this.selected = false; - this.expanded = false; - - SQL.Visual.prototype.init.apply(this); - - this.data.type = 0; - this.data.size = ""; - this.data.def = null; - this.data.nll = true; - this.data.ai = false; - this.data.comment = ""; - - if (data) { this.update(data); } - this.setTitle(title); -} - -SQL.Row.prototype._build = function() { - this.dom.container = OZ.DOM.elm("tbody"); - - this.dom.content = OZ.DOM.elm("tr"); - this.dom.selected = OZ.DOM.elm("div", {className:"selected",innerHTML:"» "}); - this.dom.title = OZ.DOM.elm("div", {className:"title"}); - var td1 = OZ.DOM.elm("td"); - var td2 = OZ.DOM.elm("td", {className:"typehint"}); - this.dom.typehint = td2; - - OZ.DOM.append( - [this.dom.container, this.dom.content], - [this.dom.content, td1, td2], - [td1, this.dom.selected, this.dom.title] - ); - - this.enter = this.bind(this.enter); - this.changeComment = this.bind(this.changeComment); - - OZ.Event.add(this.dom.container, "click",this.bind(this.click)); - OZ.Event.add(this.dom.container, "dblclick",this.bind(this.dblclick)); -} - -SQL.Row.prototype.select = function() { - if (this.selected) { return; } - this.selected = true; - this.redraw(); -} - -SQL.Row.prototype.deselect = function() { - if (!this.selected) { return; } - this.selected = false; - this.redraw(); - this.collapse(); -} - -SQL.Row.prototype.setTitle = function(t) { - var old = this.getTitle(); - for (var i=0;i<this.relations.length;i++) { - var r = this.relations[i]; - if (r.row1 != this) { continue; } - var tt = r.row2.getTitle().replace(new RegExp(old,"g"),t); - if (tt != r.row2.getTitle()) { r.row2.setTitle(tt); } - } - - SQL.Visual.prototype.setTitle.apply(this, [t]); -} - -SQL.Row.prototype.click = function(e) { /* clicked on row */ - this.dispatch("rowclick", this); - this.owner.owner.rowManager.select(this); -} - -SQL.Row.prototype.dblclick = function(e) { /* dblclicked on row */ - OZ.Event.prevent(e); - OZ.Event.stop(e); - this.expand(); -} - -SQL.Row.prototype.update = function(data) { /* update subset of row data */ - var des = SQL.Designer; - if (data.nll && data.def && data.def.match(/^null$/i)) { data.def = null; } - - for (var p in data) { this.data[p] = data[p]; } - if (!this.data.nll && this.data.def === null) { this.data.def = ""; } - - var elm = this.getDataType(); - for (var i=0;i<this.relations.length;i++) { - var r = this.relations[i]; - if (r.row1 == this) { r.row2.update({type:des.getFKTypeFor(this.data.type),size:this.data.size}); } - } - this.redraw(); -} - -SQL.Row.prototype.up = function() { /* shift up */ - var r = this.owner.rows; - var idx = r.indexOf(this); - if (!idx) { return; } - r[idx-1].dom.container.parentNode.insertBefore(this.dom.container,r[idx-1].dom.container); - r.splice(idx,1); - r.splice(idx-1,0,this); - this.redraw(); -} - -SQL.Row.prototype.down = function() { /* shift down */ - var r = this.owner.rows; - var idx = r.indexOf(this); - if (idx+1 == this.owner.rows.length) { return; } - r[idx].dom.container.parentNode.insertBefore(this.dom.container,r[idx+1].dom.container.nextSibling); - r.splice(idx,1); - r.splice(idx+1,0,this); - this.redraw(); -} - -SQL.Row.prototype.buildEdit = function() { - OZ.DOM.clear(this.dom.container); - - var elms = []; - this.dom.name = OZ.DOM.elm("input"); - this.dom.name.type = "text"; - elms.push(["name",this.dom.name]); - OZ.Event.add(this.dom.name, "keypress", this.enter); - - this.dom.type = this.buildTypeSelect(this.data.type); - elms.push(["type",this.dom.type]); - - this.dom.size = OZ.DOM.elm("input"); - this.dom.size.type = "text"; - elms.push(["size",this.dom.size]); - - this.dom.def = OZ.DOM.elm("input"); - this.dom.def.type = "text"; - elms.push(["def",this.dom.def]); - - this.dom.ai = OZ.DOM.elm("input"); - this.dom.ai.type = "checkbox"; - elms.push(["ai",this.dom.ai]); - - this.dom.nll = OZ.DOM.elm("input"); - this.dom.nll.type = "checkbox"; - elms.push(["null",this.dom.nll]); - - this.dom.comment = OZ.DOM.elm("span",{className:"comment"}); - this.dom.comment.innerHTML = this.data.comment; - - this.dom.commentbtn = OZ.DOM.elm("input"); - this.dom.commentbtn.type = "button"; - this.dom.commentbtn.value = _("comment"); - - OZ.Event.add(this.dom.commentbtn, "click", this.changeComment); - - for (var i=0;i<elms.length;i++) { - var row = elms[i]; - var tr = OZ.DOM.elm("tr"); - var td1 = OZ.DOM.elm("td"); - var td2 = OZ.DOM.elm("td"); - var l = OZ.DOM.text(_(row[0])+": "); - OZ.DOM.append( - [tr, td1, td2], - [td1, l], - [td2, row[1]] - ); - this.dom.container.appendChild(tr); - } - - var tr = OZ.DOM.elm("tr"); - var td1 = OZ.DOM.elm("td"); - var td2 = OZ.DOM.elm("td"); - OZ.DOM.append( - [tr, td1, td2], - [td1, this.dom.comment], - [td2, this.dom.commentbtn] - ); - this.dom.container.appendChild(tr); -} - -SQL.Row.prototype.changeComment = function(e) { - var c = prompt(_("commenttext"),this.data.comment); - if (c === null) { return; } - this.data.comment = c; - this.dom.comment.innerHTML = this.data.comment; -} - -SQL.Row.prototype.expand = function() { - if (this.expanded) { return; } - this.expanded = true; - this.buildEdit(); - this.load(); - this.redraw(); - this.dom.name.focus(); - this.dom.name.select(); -} - -SQL.Row.prototype.collapse = function() { - if (!this.expanded) { return; } - this.expanded = false; - - var data = { - type: this.dom.type.selectedIndex, - def: this.dom.def.value, - size: this.dom.size.value, - nll: this.dom.nll.checked, - ai: this.dom.ai.checked - } - - OZ.DOM.clear(this.dom.container); - this.dom.container.appendChild(this.dom.content); - - this.update(data); - this.setTitle(this.dom.name.value); -} - -SQL.Row.prototype.load = function() { /* put data to expanded form */ - this.dom.name.value = this.getTitle(); - var def = this.data.def; - if (def === null) { def = "NULL"; } - - this.dom.def.value = def; - this.dom.size.value = this.data.size; - this.dom.nll.checked = this.data.nll; - this.dom.ai.checked = this.data.ai; -} - -SQL.Row.prototype.redraw = function() { - var color = this.getColor(); - this.dom.container.style.backgroundColor = color; - OZ.DOM.removeClass(this.dom.title, "primary"); - OZ.DOM.removeClass(this.dom.title, "key"); - if (this.isPrimary()) { OZ.DOM.addClass(this.dom.title, "primary"); } - if (this.isKey()) { OZ.DOM.addClass(this.dom.title, "key"); } - this.dom.selected.style.display = (this.selected ? "" : "none"); - this.dom.container.title = this.data.comment; - - var typehint = []; - if (this.owner.owner.getOption("showtype")) { - var elm = this.getDataType(); - typehint.push(elm.getAttribute("sql")); - } - - if (this.owner.owner.getOption("showsize") && this.data.size) { - typehint.push("(" + this.data.size + ")"); - } - - this.dom.typehint.innerHTML = typehint.join(" "); - this.owner.redraw(); - this.owner.owner.rowManager.redraw(); -} - -SQL.Row.prototype.addRelation = function(r) { - this.relations.push(r); -} - -SQL.Row.prototype.removeRelation = function(r) { - var idx = this.relations.indexOf(r); - if (idx == -1) { return; } - this.relations.splice(idx,1); -} - -SQL.Row.prototype.addKey = function(k) { - this.keys.push(k); - this.redraw(); -} - -SQL.Row.prototype.removeKey = function(k) { - var idx = this.keys.indexOf(k); - if (idx == -1) { return; } - this.keys.splice(idx,1); - this.redraw(); -} - -SQL.Row.prototype.getDataType = function() { - var type = this.data.type; - var elm = DATATYPES.getElementsByTagName("type")[type]; - return elm; -} - -SQL.Row.prototype.getColor = function() { - var elm = this.getDataType(); - var g = this.getDataType().parentNode; - return elm.getAttribute("color") || g.getAttribute("color") || "#fff"; -} - -SQL.Row.prototype.buildTypeSelect = function(id) { /* build selectbox with avail datatypes */ - var s = OZ.DOM.elm("select"); - var gs = DATATYPES.getElementsByTagName("group"); - for (var i=0;i<gs.length;i++) { - var g = gs[i]; - var og = OZ.DOM.elm("optgroup"); - og.style.backgroundColor = g.getAttribute("color") || "#fff"; - og.label = g.getAttribute("label"); - s.appendChild(og); - var ts = g.getElementsByTagName("type"); - for (var j=0;j<ts.length;j++) { - var t = ts[j]; - var o = OZ.DOM.elm("option"); - if (t.getAttribute("color")) { o.style.backgroundColor = t.getAttribute("color"); } - if (t.getAttribute("note")) { o.title = t.getAttribute("note"); } - o.innerHTML = t.getAttribute("label"); - og.appendChild(o); - } - } - s.selectedIndex = id; - return s; -} - -SQL.Row.prototype.destroy = function() { - SQL.Visual.prototype.destroy.apply(this); - while (this.relations.length) { - this.owner.owner.removeRelation(this.relations[0]); - } - for (var i=0;i<this.keys.length;i++){ - this.keys[i].removeRow(this); - } -} - -SQL.Row.prototype.toXML = function() { - var xml = ""; - - var t = this.getTitle().replace(/"/g,"""); - var nn = (this.data.nll ? "1" : "0"); - var ai = (this.data.ai ? "1" : "0"); - xml += '<row name="'+t+'" null="'+nn+'" autoincrement="'+ai+'">\n'; - - var elm = this.getDataType(); - var t = elm.getAttribute("sql"); - if (this.data.size.length) { t += "("+this.data.size+")"; } - xml += "<datatype>"+t+"</datatype>\n"; - - if (this.data.def || this.data.def === null) { - var q = elm.getAttribute("quote"); - var d = this.data.def; - if (d === null) { - d = "NULL"; - } else if (d != "CURRENT_TIMESTAMP") { - d = q+d+q; - } - xml += "<default>"+d+"</default>"; - } - - for (var i=0;i<this.relations.length;i++) { - var r = this.relations[i]; - if (r.row2 != this) { continue; } - xml += '<relation table="'+r.row1.owner.getTitle()+'" row="'+r.row1.getTitle()+'" />\n'; - } - - if (this.data.comment) { - var escaped = this.data.comment.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<"); - xml += "<comment>"+escaped+"</comment>\n"; - } - - xml += "</row>\n"; - return xml; -} - -SQL.Row.prototype.fromXML = function(node) { - var name = node.getAttribute("name"); - - var obj = { type:0, size:"" }; - obj.nll = (node.getAttribute("null") == "1"); - obj.ai = (node.getAttribute("autoincrement") == "1"); - - var cs = node.getElementsByTagName("comment"); - if (cs.length && cs[0].firstChild) { obj.comment = cs[0].firstChild.nodeValue; } - - var d = node.getElementsByTagName("datatype"); - if (d.length && d[0].firstChild) { - var s = d[0].firstChild.nodeValue; - var r = s.match(/^([^\(]+)(\((.*)\))?.*$/); - var type = r[1]; - if (r[3]) { obj.size = r[3]; } - var types = window.DATATYPES.getElementsByTagName("type"); - for (var i=0;i<types.length;i++) { - var sql = types[i].getAttribute("sql"); - var re = types[i].getAttribute("re"); - if (sql == type || (re && new RegExp(re).exec(type)) ) { obj.type = i; } - } - } - - var elm = DATATYPES.getElementsByTagName("type")[obj.type]; - var d = node.getElementsByTagName("default"); - if (d.length && d[0].firstChild) { - var def = d[0].firstChild.nodeValue; - obj.def = def; - var q = elm.getAttribute("quote"); - if (q) { - var re = new RegExp("^"+q+"(.*)"+q+"$"); - var r = def.match(re); - if (r) { obj.def = r[1]; } - } - } - - this.update(obj); - this.setTitle(name); -} - -SQL.Row.prototype.isPrimary = function() { - for (var i=0;i<this.keys.length;i++) { - var k = this.keys[i]; - if (k.getType() == "PRIMARY") { return true; } - } - return false; -} - -SQL.Row.prototype.isUnique = function() { - for (var i=0;i<this.keys.length;i++) { - var k = this.keys[i]; - var t = k.getType(); - if (t == "PRIMARY" || t == "UNIQUE") { return true; } - } - return false; -} - -SQL.Row.prototype.isKey = function() { - return this.keys.length > 0; -} - -SQL.Row.prototype.enter = function(e) { - if (e.keyCode == 13) { - this.collapse(); - } -} - -/* --------------------------- relation (connector) ----------- */ - -SQL.Relation = OZ.Class().extend(SQL.Visual); -SQL.Relation._counter = 0; -SQL.Relation.prototype.init = function(owner, row1, row2) { - this.owner = owner; - this.row1 = row1; - this.row2 = row2; - this.color = "#000"; - this.hidden = false; - SQL.Visual.prototype.init.apply(this); - - /* if one of the rows already has relations, inherit color */ - var all = row1.relations.concat(row2.relations); - if (all.length) { /* inherit */ - this.color = all[0].getColor(); - } else if (CONFIG.RELATION_COLORS) { /* pick next */ - this.constructor._counter++; - var colorIndex = this.constructor._counter - 1; - this.color = CONFIG.RELATION_COLORS[colorIndex % CONFIG.RELATION_COLORS.length]; - } - - this.row1.addRelation(this); - this.row2.addRelation(this); - this.dom = []; - - if (this.owner.vector) { - var path = document.createElementNS(this.owner.svgNS, "path"); - path.setAttribute("stroke", this.color); - path.setAttribute("stroke-width", CONFIG.RELATION_THICKNESS); - path.setAttribute("fill", "none"); - this.owner.dom.svg.appendChild(path); - this.dom.push(path); - } else { - for (var i=0;i<3;i++) { - var div = OZ.DOM.elm("div",{position:"absolute",className:"relation",backgroundColor:this.color}); - this.dom.push(div); - if (i & 1) { /* middle */ - OZ.Style.set(div, {width:CONFIG.RELATION_THICKNESS+"px"}); - } else { /* first & last */ - OZ.Style.set(div, {height:CONFIG.RELATION_THICKNESS+"px"}); - } - this.owner.dom.container.appendChild(div); - } - } - - this.redraw(); -} - -SQL.Relation.prototype.getColor = function() { - return this.color; -} - -SQL.Relation.prototype.show = function() { - this.hidden = false; - for (var i=0;i<this.dom.length;i++) { - this.dom[i].style.visibility = ""; - } -} - -SQL.Relation.prototype.hide = function() { - this.hidden = true; - for (var i=0;i<this.dom.length;i++) { - this.dom[i].style.visibility = "hidden"; - } -} - -SQL.Relation.prototype.redrawNormal = function(p1, p2, half) { - if (this.owner.vector) { - var str = "M "+p1[0]+" "+p1[1]+" C "+(p1[0] + half)+" "+p1[1]+" "; - str += (p2[0]-half)+" "+p2[1]+" "+p2[0]+" "+p2[1]; - this.dom[0].setAttribute("d",str); - } else { - this.dom[0].style.left = p1[0]+"px"; - this.dom[0].style.top = p1[1]+"px"; - this.dom[0].style.width = half+"px"; - - this.dom[1].style.left = (p1[0] + half) + "px"; - this.dom[1].style.top = Math.min(p1[1],p2[1]) + "px"; - this.dom[1].style.height = (Math.abs(p1[1] - p2[1])+CONFIG.RELATION_THICKNESS)+"px"; - - this.dom[2].style.left = (p1[0]+half+1)+"px"; - this.dom[2].style.top = p2[1]+"px"; - this.dom[2].style.width = half+"px"; - } -} - -SQL.Relation.prototype.redrawSide = function(p1, p2, x) { - if (this.owner.vector) { - var str = "M "+p1[0]+" "+p1[1]+" C "+x+" "+p1[1]+" "; - str += x+" "+p2[1]+" "+p2[0]+" "+p2[1]; - this.dom[0].setAttribute("d",str); - } else { - this.dom[0].style.left = Math.min(x,p1[0])+"px"; - this.dom[0].style.top = p1[1]+"px"; - this.dom[0].style.width = Math.abs(p1[0]-x)+"px"; - - this.dom[1].style.left = x+"px"; - this.dom[1].style.top = Math.min(p1[1],p2[1]) + "px"; - this.dom[1].style.height = (Math.abs(p1[1] - p2[1])+CONFIG.RELATION_THICKNESS)+"px"; - - this.dom[2].style.left = Math.min(x,p2[0])+"px"; - this.dom[2].style.top = p2[1]+"px"; - this.dom[2].style.width = Math.abs(p2[0]-x)+"px"; - } -} - -SQL.Relation.prototype.redraw = function() { /* draw connector */ - if (this.hidden) { return; } - var t1 = this.row1.owner.dom.container; - var t2 = this.row2.owner.dom.container; - - var l1 = t1.offsetLeft; - var l2 = t2.offsetLeft; - var r1 = l1 + t1.offsetWidth; - var r2 = l2 + t2.offsetWidth; - var t1 = t1.offsetTop + this.row1.dom.container.offsetTop + Math.round(this.row1.dom.container.offsetHeight/2); - var t2 = t2.offsetTop + this.row2.dom.container.offsetTop + Math.round(this.row2.dom.container.offsetHeight/2); - - if (this.row1.owner.selected) { t1++; l1++; r1--; } - if (this.row2.owner.selected) { t2++; l2++; r2--; } - - var p1 = [0,0]; - var p2 = [0,0]; - - if (r1 < l2 || r2 < l1) { /* between tables */ - if (Math.abs(r1 - l2) < Math.abs(r2 - l1)) { - p1 = [r1,t1]; - p2 = [l2,t2]; - } else { - p1 = [r2,t2]; - p2 = [l1,t1]; - } - var half = Math.floor((p2[0] - p1[0])/2); - this.redrawNormal(p1, p2, half); - } else { /* next to tables */ - var x = 0; - var l = 0; - if (Math.abs(l1 - l2) < Math.abs(r1 - r2)) { /* left of tables */ - p1 = [l1,t1]; - p2 = [l2,t2]; - x = Math.min(l1,l2) - CONFIG.RELATION_SPACING; - } else { /* right of tables */ - p1 = [r1,t1]; - p2 = [r2,t2]; - x = Math.max(r1,r2) + CONFIG.RELATION_SPACING; - } - this.redrawSide(p1, p2, x); - } /* line next to tables */ -} - -SQL.Relation.prototype.destroy = function() { - this.row1.removeRelation(this); - this.row2.removeRelation(this); - for (var i=0;i<this.dom.length;i++) { - this.dom[i].parentNode.removeChild(this.dom[i]); - } -} - -/* --------------------- db table ------------ */ - -SQL.Table = OZ.Class().extend(SQL.Visual); - -SQL.Table.prototype.init = function(owner, name, x, y, z) { - this.owner = owner; - this.rows = []; - this.keys = []; - this.zIndex = 0; - this._ec = []; - - this.flag = false; - this.selected = false; - SQL.Visual.prototype.init.apply(this); - this.data.comment = ""; - - this.setTitle(name); - this.x = x || 0; - this.y = y || 0; - this.setZ(z); - this.snap(); -} - -SQL.Table.prototype._build = function() { - this.dom.container = OZ.DOM.elm("div", {className:"table"}); - this.dom.content = OZ.DOM.elm("table"); - var thead = OZ.DOM.elm("thead"); - var tr = OZ.DOM.elm("tr"); - this.dom.title = OZ.DOM.elm("td", {className:"title", colSpan:2}); - - OZ.DOM.append( - [this.dom.container, this.dom.content], - [this.dom.content, thead], - [thead, tr], - [tr, this.dom.title] - ); - - this.dom.mini = OZ.DOM.elm("div", {className:"mini"}); - this.owner.map.dom.container.appendChild(this.dom.mini); - - this._ec.push(OZ.Event.add(this.dom.container, "click", this.bind(this.click))); - this._ec.push(OZ.Event.add(this.dom.container, "dblclick", this.bind(this.dblclick))); - this._ec.push(OZ.Event.add(this.dom.container, "mousedown", this.bind(this.down))); - this._ec.push(OZ.Event.add(this.dom.container, "touchstart", this.bind(this.down))); - this._ec.push(OZ.Event.add(this.dom.container, "touchmove", OZ.Event.prevent)); -} - -SQL.Table.prototype.setTitle = function(t) { - var old = this.getTitle(); - for (var i=0;i<this.rows.length;i++) { - var row = this.rows[i]; - for (var j=0;j<row.relations.length;j++) { - var r = row.relations[j]; - if (r.row1 != row) { continue; } - var tt = row.getTitle().replace(new RegExp(old,"g"),t); - if (tt != row.getTitle()) { row.setTitle(tt); } - } - } - SQL.Visual.prototype.setTitle.apply(this, [t]); -} - -SQL.Table.prototype.getRelations = function() { - var arr = []; - for (var i=0;i<this.rows.length;i++) { - var row = this.rows[i]; - for (var j=0;j<row.relations.length;j++) { - var r = row.relations[j]; - if (arr.indexOf(r) == -1) { arr.push(r); } - } - } - return arr; -} - -SQL.Table.prototype.showRelations = function() { - var rs = this.getRelations(); - for (var i=0;i<rs.length;i++) { rs[i].show(); } -} - -SQL.Table.prototype.hideRelations = function() { - var rs = this.getRelations(); - for (var i=0;i<rs.length;i++) { rs[i].hide(); } -} - -SQL.Table.prototype.click = function(e) { - OZ.Event.stop(e); - var t = OZ.Event.target(e); - this.owner.tableManager.select(this); - - if (t != this.dom.title) { return; } /* click on row */ - - this.dispatch("tableclick",this); - this.owner.rowManager.select(false); -} - -SQL.Table.prototype.dblclick = function(e) { - var t = OZ.Event.target(e); - if (t == this.dom.title) { this.owner.tableManager.edit(); } -} - -SQL.Table.prototype.select = function() { - if (this.selected) { return; } - this.selected = true; - OZ.DOM.addClass(this.dom.container, "selected"); - OZ.DOM.addClass(this.dom.mini, "mini_selected"); - this.redraw(); -} - -SQL.Table.prototype.deselect = function() { - if (!this.selected) { return; } - this.selected = false; - OZ.DOM.removeClass(this.dom.container, "selected"); - OZ.DOM.removeClass(this.dom.mini, "mini_selected"); - this.redraw(); -} - -SQL.Table.prototype.addRow = function(title, data) { - var r = new SQL.Row(this, title, data); - this.rows.push(r); - this.dom.content.appendChild(r.dom.container); - this.redraw(); - return r; -} - -SQL.Table.prototype.removeRow = function(r) { - var idx = this.rows.indexOf(r); - if (idx == -1) { return; } - r.destroy(); - this.rows.splice(idx,1); - this.redraw(); -} - -SQL.Table.prototype.addKey = function(name) { - var k = new SQL.Key(this, name); - this.keys.push(k); - return k; -} - -SQL.Table.prototype.removeKey = function(i) { - var idx = this.keys.indexOf(k); - if (idx == -1) { return; } - k.destroy(); - this.keys.splice(idx,1); -} - -SQL.Table.prototype.redraw = function() { - var x = this.x; - var y = this.y; - if (this.selected) { x--; y--; } - this.dom.container.style.left = x+"px"; - this.dom.container.style.top = y+"px"; - - var ratioX = this.owner.map.width / this.owner.width; - var ratioY = this.owner.map.height / this.owner.height; - - var w = this.dom.container.offsetWidth * ratioX; - var h = this.dom.container.offsetHeight * ratioY; - var x = this.x * ratioX; - var y = this.y * ratioY; - - this.dom.mini.style.width = Math.round(w)+"px"; - this.dom.mini.style.height = Math.round(h)+"px"; - this.dom.mini.style.left = Math.round(x)+"px"; - this.dom.mini.style.top = Math.round(y)+"px"; - - this.width = this.dom.container.offsetWidth; - this.height = this.dom.container.offsetHeight; - - var rs = this.getRelations(); - for (var i=0;i<rs.length;i++) { rs[i].redraw(); } -} - -SQL.Table.prototype.moveBy = function(dx, dy) { - this.x += dx; - this.y += dy; - - this.snap(); - this.redraw(); -} - -SQL.Table.prototype.moveTo = function(x, y) { - this.x = x; - this.y = y; - - this.snap(); - this.redraw(); -} - -SQL.Table.prototype.snap = function() { - var snap = parseInt(SQL.Designer.getOption("snap")); - if (snap) { - this.x = Math.round(this.x / snap) * snap; - this.y = Math.round(this.y / snap) * snap; - } -} - -SQL.Table.prototype.down = function(e) { /* mousedown - start drag */ - OZ.Event.stop(e); - var t = OZ.Event.target(e); - if (t != this.dom.title) { return; } /* on a row */ - - /* touch? */ - if (e.type == "touchstart") { - var event = e.touches[0]; - var moveEvent = "touchmove"; - var upEvent = "touchend"; - } else { - var event = e; - var moveEvent = "mousemove"; - var upEvent = "mouseup"; - } - - /* a non-shift click within a selection preserves the selection */ - if (e.shiftKey || ! this.selected) { - this.owner.tableManager.select(this, e.shiftKey); - } - - var t = SQL.Table; - t.active = this.owner.tableManager.selection; - var n = t.active.length; - t.x = new Array(n); - t.y = new Array(n); - for (var i=0;i<n;i++) { - /* position relative to mouse cursor */ - t.x[i] = t.active[i].x - event.clientX; - t.y[i] = t.active[i].y - event.clientY; - } - - if (this.owner.getOption("hide")) { - for (var i=0;i<n;i++) { - t.active[i].hideRelations(); - } - } - - this.documentMove = OZ.Event.add(document, moveEvent, this.bind(this.move)); - this.documentUp = OZ.Event.add(document, upEvent, this.bind(this.up)); -} - -SQL.Table.prototype.toXML = function() { - var t = this.getTitle().replace(/"/g,"""); - var xml = ""; - xml += '<table x="'+this.x+'" y="'+this.y+'" name="'+t+'">\n'; - for (var i=0;i<this.rows.length;i++) { - xml += this.rows[i].toXML(); - } - for (var i=0;i<this.keys.length;i++) { - xml += this.keys[i].toXML(); - } - var c = this.getComment(); - if (c) { - c = c.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<"); - xml += "<comment>"+c+"</comment>\n"; - } - xml += "</table>\n"; - return xml; -} - -SQL.Table.prototype.fromXML = function(node) { - var name = node.getAttribute("name"); - this.setTitle(name); - var x = parseInt(node.getAttribute("x")) || 0; - var y = parseInt(node.getAttribute("y")) || 0; - this.moveTo(x, y); - var rows = node.getElementsByTagName("row"); - for (var i=0;i<rows.length;i++) { - var row = rows[i]; - var r = this.addRow(""); - r.fromXML(row); - } - var keys = node.getElementsByTagName("key"); - for (var i=0;i<keys.length;i++) { - var key = keys[i]; - var k = this.addKey(); - k.fromXML(key); - } - for (var i=0;i<node.childNodes.length;i++) { - var ch = node.childNodes[i]; - if (ch.tagName && ch.tagName.toLowerCase() == "comment" && ch.firstChild) { - this.setComment(ch.firstChild.nodeValue); - } - } -} - -SQL.Table.prototype.getZ = function() { - return this.zIndex; -} - -SQL.Table.prototype.setZ = function(z) { - this.zIndex = z; - this.dom.container.style.zIndex = z; -} - -SQL.Table.prototype.findNamedRow = function(n) { /* return row with a given name */ - for (var i=0;i<this.rows.length;i++) { - if (this.rows[i].getTitle() == n) { return this.rows[i]; } - } - return false; -} - -SQL.Table.prototype.addKey = function(type, name) { - var i = new SQL.Key(this, type, name); - this.keys.push(i); - return i; -} - -SQL.Table.prototype.removeKey = function(i) { - var idx = this.keys.indexOf(i); - if (idx == -1) { return; } - i.destroy(); - this.keys.splice(idx,1); -} - -SQL.Table.prototype.setComment = function(c) { - this.data.comment = c; - this.dom.title.title = this.data.comment; -} - -SQL.Table.prototype.getComment = function() { - return this.data.comment; -} - -SQL.Table.prototype.move = function(e) { /* mousemove */ - var t = SQL.Table; - SQL.Designer.removeSelection(); - if (e.type == "touchmove") { - if (e.touches.length > 1) { return; } - var event = e.touches[0]; - } else { - var event = e; - } - - for (var i=0;i<t.active.length;i++) { - var x = t.x[i] + event.clientX; - var y = t.y[i] + event.clientY; - x = Math.max(x, 0); - y = Math.max(y, 0); - t.active[i].moveTo(x,y); - } -} - -SQL.Table.prototype.up = function(e) { - var t = SQL.Table; - var d = SQL.Designer; - if (d.getOption("hide")) { - for (var i=0;i<t.active.length;i++) { - t.active[i].showRelations(); - t.active[i].redraw(); - } - } - t.active = false; - OZ.Event.remove(this.documentMove); - OZ.Event.remove(this.documentUp); - this.owner.sync(); -} - -SQL.Table.prototype.destroy = function() { - SQL.Visual.prototype.destroy.apply(this); - this.dom.mini.parentNode.removeChild(this.dom.mini); - while (this.rows.length) { - this.removeRow(this.rows[0]); - } - this._ec.forEach(OZ.Event.remove, OZ.Event); -} - -/* --------------------- db index ------------ */ - -SQL.Key = OZ.Class().extend(SQL.Visual); - -SQL.Key.prototype.init = function(owner, type, name) { - this.owner = owner; - this.rows = []; - this.type = type || "INDEX"; - this.name = name || ""; - SQL.Visual.prototype.init.apply(this); -} - -SQL.Key.prototype.setName = function(n) { - this.name = n; -} - -SQL.Key.prototype.getName = function() { - return this.name; -} - -SQL.Key.prototype.setType = function(t) { - if (!t) { return; } - this.type = t; - for (var i=0;i<this.rows.length;i++) { this.rows[i].redraw(); } -} - -SQL.Key.prototype.getType = function() { - return this.type; -} - -SQL.Key.prototype.addRow = function(r) { - if (r.owner != this.owner) { return; } - this.rows.push(r); - r.addKey(this); -} - -SQL.Key.prototype.removeRow = function(r) { - var idx = this.rows.indexOf(r); - if (idx == -1) { return; } - r.removeKey(this); - this.rows.splice(idx,1); -} - -SQL.Key.prototype.destroy = function() { - for (var i=0;i<this.rows.length;i++) { - this.rows[i].removeKey(this); - } -} - -SQL.Key.prototype.getLabel = function() { - return this.name || this.type; -} - -SQL.Key.prototype.toXML = function() { - var xml = ""; - xml += '<key type="'+this.getType()+'" name="'+this.getName()+'">\n'; - for (var i=0;i<this.rows.length;i++) { - var r = this.rows[i]; - xml += '<part>'+r.getTitle()+'</part>\n'; - } - xml += '</key>\n'; - return xml; -} - -SQL.Key.prototype.fromXML = function(node) { - this.setType(node.getAttribute("type")); - this.setName(node.getAttribute("name")); - var parts = node.getElementsByTagName("part"); - for (var i=0;i<parts.length;i++) { - var name = parts[i].firstChild.nodeValue; - var row = this.owner.findNamedRow(name); - this.addRow(row); - } -} - -/* --------------------- rubberband -------------------- */ - -SQL.Rubberband = OZ.Class().extend(SQL.Visual); - -SQL.Rubberband.prototype.init = function(owner) { - this.owner = owner; - SQL.Visual.prototype.init.apply(this); - this.dom.container = OZ.$("rubberband"); - OZ.Event.add("area", "mousedown", this.bind(this.down)); -} - -SQL.Rubberband.prototype.down = function(e) { - OZ.Event.prevent(e); - var scroll = OZ.DOM.scroll(); - this.x = this.x0 = e.clientX + scroll[0]; - this.y = this.y0 = e.clientY + scroll[1]; - this.width = 0; - this.height = 0; - this.redraw(); - this.documentMove = OZ.Event.add(document, "mousemove", this.bind(this.move)); - this.documentUp = OZ.Event.add(document, "mouseup", this.bind(this.up)); -} - -SQL.Rubberband.prototype.move = function(e) { - var scroll = OZ.DOM.scroll(); - var x = e.clientX + scroll[0]; - var y = e.clientY + scroll[1]; - this.width = Math.abs(x-this.x0); - this.height = Math.abs(y-this.y0); - if (x<this.x0) { this.x = x; } else { this.x = this.x0; } - if (y<this.y0) { this.y = y; } else { this.y = this.y0; } - this.redraw(); - this.dom.container.style.visibility = "visible"; -} - -SQL.Rubberband.prototype.up = function(e) { - OZ.Event.prevent(e); - this.dom.container.style.visibility = "hidden"; - OZ.Event.remove(this.documentMove); - OZ.Event.remove(this.documentUp); - this.owner.tableManager.selectRect(this.x, this.y, this.width, this.height); -} - -SQL.Rubberband.prototype.redraw = function() { - this.dom.container.style.left = this.x+"px"; - this.dom.container.style.top = this.y+"px"; - this.dom.container.style.width = this.width+"px"; - this.dom.container.style.height = this.height+"px"; -} - -/* --------------------- minimap ------------ */ - -SQL.Map = OZ.Class().extend(SQL.Visual); - -SQL.Map.prototype.init = function(owner) { - this.owner = owner; - SQL.Visual.prototype.init.apply(this); - this.dom.container = OZ.$("minimap"); - this.width = this.dom.container.offsetWidth - 2; - this.height = this.dom.container.offsetHeight - 2; - - this.dom.port = OZ.DOM.elm("div",{className:"port", zIndex:1}); - this.dom.container.appendChild(this.dom.port); - this.sync = this.bind(this.sync); - - this.flag = false; - this.sync(); - - OZ.Event.add(window, "resize", this.sync); - OZ.Event.add(window, "scroll", this.sync); - OZ.Event.add(this.dom.container, "mousedown", this.bind(this.down)); - OZ.Event.add(this.dom.container, "touchstart", this.bind(this.down)); - OZ.Event.add(this.dom.container, "touchmove", OZ.Event.prevent); -} - -SQL.Map.prototype.down = function(e) { /* mousedown - move view and start drag */ - this.flag = true; - this.dom.container.style.cursor = "move"; - var pos = OZ.DOM.pos(this.dom.container); - - this.x = Math.round(pos[0] + this.l + this.w/2); - this.y = Math.round(pos[1] + this.t + this.h/2); - this.move(e); - - if (e.type == "touchstart") { - var eventMove = "touchmove"; - var eventUp = "touchend"; - } else { - var eventMove = "mousemove"; - var eventUp = "mouseup"; - } - - this.documentMove = OZ.Event.add(document, eventMove, this.bind(this.move)); - this.documentUp = OZ.Event.add(document, eventUp, this.bind(this.up)); -} - -SQL.Map.prototype.move = function(e) { /* mousemove */ - if (!this.flag) { return; } - OZ.Event.prevent(e); - - if (e.type.match(/touch/)) { - if (e.touches.length > 1) { return; } - var event = e.touches[0]; - } else { - var event = e; - } - - var dx = event.clientX - this.x; - var dy = event.clientY - this.y; - if (this.l + dx < 0) { dx = -this.l; } - if (this.t + dy < 0) { dy = -this.t; } - if (this.l + this.w + 4 + dx > this.width) { dx = this.width - 4 - this.l - this.w; } - if (this.t + this.h + 4 + dy > this.height) { dy = this.height - 4 - this.t - this.h; } - - - this.x += dx; - this.y += dy; - - this.l += dx; - this.t += dy; - - var coefX = this.width / this.owner.width; - var coefY = this.height / this.owner.height; - var left = this.l / coefX; - var top = this.t / coefY; - - if (OZ.webkit) { - document.body.scrollLeft = Math.round(left); - document.body.scrollTop = Math.round(top); - } else { - document.documentElement.scrollLeft = Math.round(left); - document.documentElement.scrollTop = Math.round(top); - } - - this.redraw(); -} - -SQL.Map.prototype.up = function(e) { /* mouseup */ - this.flag = false; - this.dom.container.style.cursor = ""; - OZ.Event.remove(this.documentMove); - OZ.Event.remove(this.documentUp); -} - -SQL.Map.prototype.sync = function() { /* when window changes, adjust map */ - var dims = OZ.DOM.win(); - var scroll = OZ.DOM.scroll(); - var scaleX = this.width / this.owner.width; - var scaleY = this.height / this.owner.height; - - var w = dims[0] * scaleX - 4 - 0; - var h = dims[1] * scaleY - 4 - 0; - var x = scroll[0] * scaleX; - var y = scroll[1] * scaleY; - - this.w = Math.round(w); - this.h = Math.round(h); - this.l = Math.round(x); - this.t = Math.round(y); - - this.redraw(); -} - -SQL.Map.prototype.redraw = function() { - this.dom.port.style.width = this.w+"px"; - this.dom.port.style.height = this.h+"px"; - this.dom.port.style.left = this.l+"px"; - this.dom.port.style.top = this.t+"px"; -} - -/* --------------------- io ------------ */ - -SQL.IO = OZ.Class(); - -SQL.IO.prototype.init = function(owner) { - this.owner = owner; - this._name = ""; /* last used name with server load/save */ - this.lastUsedName = ""; /* last used name with local storage or dropbox load/save */ - this.dom = { - container:OZ.$("io") - }; - - var ids = ["saveload","clientlocalsave", "clientsave", "clientlocalload", "clientlocallist","clientload", "clientsql", - "dropboxsave", "dropboxload", "dropboxlist", - "quicksave", "serversave", "serverload", - "serverlist", "serverimport"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - this.dom[id] = elm; - elm.value = _(id); - } - - this.dom.quicksave.value += " (F2)"; - - var ids = ["client","server","output","backendlabel"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - elm.innerHTML = _(id); - } - - this.dom.ta = OZ.$("textarea"); - this.dom.backend = OZ.$("backend"); - - this.dom.container.parentNode.removeChild(this.dom.container); - this.dom.container.style.visibility = ""; - - this.saveresponse = this.bind(this.saveresponse); - this.loadresponse = this.bind(this.loadresponse); - this.listresponse = this.bind(this.listresponse); - this.importresponse = this.bind(this.importresponse); - - OZ.Event.add(this.dom.saveload, "click", this.bind(this.click)); - OZ.Event.add(this.dom.clientlocalsave, "click", this.bind(this.clientlocalsave)); - OZ.Event.add(this.dom.clientsave, "click", this.bind(this.clientsave)); - OZ.Event.add(this.dom.clientlocalload, "click", this.bind(this.clientlocalload)); - OZ.Event.add(this.dom.clientlocallist, "click", this.bind(this.clientlocallist)); - OZ.Event.add(this.dom.clientload, "click", this.bind(this.clientload)); - OZ.Event.add(this.dom.dropboxload, "click", this.bind(this.dropboxload)); - OZ.Event.add(this.dom.dropboxsave, "click", this.bind(this.dropboxsave)); - OZ.Event.add(this.dom.dropboxlist, "click", this.bind(this.dropboxlist)); - OZ.Event.add(this.dom.clientsql, "click", this.bind(this.clientsql)); - OZ.Event.add(this.dom.quicksave, "click", this.bind(this.quicksave)); - OZ.Event.add(this.dom.serversave, "click", this.bind(this.serversave)); - OZ.Event.add(this.dom.serverload, "click", this.bind(this.serverload)); - OZ.Event.add(this.dom.serverlist, "click", this.bind(this.serverlist)); - OZ.Event.add(this.dom.serverimport, "click", this.bind(this.serverimport)); - OZ.Event.add(document, "keydown", this.bind(this.press)); - this.build(); - - this.dropBoxInit (); -} - -SQL.IO.prototype.build = function() { - OZ.DOM.clear(this.dom.backend); - - var bs = CONFIG.AVAILABLE_BACKENDS; - var be = CONFIG.DEFAULT_BACKEND; - var r = window.location.search.substring(1).match(/backend=([^&]*)/); - if (r) { - req = r[1]; - if (bs.indexOf(req) != -1) { - be = req; - } - } - for (var i=0;i<bs.length;i++) { - var o = OZ.DOM.elm("option"); - o.value = bs[i]; - o.innerHTML = bs[i]; - this.dom.backend.appendChild(o); - if (bs[i] == be) { this.dom.backend.selectedIndex = i; } - } -} - -SQL.IO.prototype.click = function() { /* open io dialog */ - this.build(); - this.dom.ta.value = ""; - this.dom.clientsql.value = _("clientsql") + " (" + window.DATATYPES.getAttribute("db") + ")"; - this.owner.window.open(_("saveload"),this.dom.container); -} - -SQL.IO.prototype.fromXMLText = function(xml) { - try { - if (window.DOMParser) { - var parser = new DOMParser(); - var xmlDoc = parser.parseFromString(xml, "text/xml"); - } else if (window.ActiveXObject || "ActiveXObject" in window) { - var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); - xmlDoc.loadXML(xml); - } else { - throw new Error("No XML parser available."); - } - } catch(e) { - alert(_("xmlerror")+': '+e.message); - return; - } - this.fromXML(xmlDoc); -} - -SQL.IO.prototype.fromXML = function(xmlDoc) { - if (!xmlDoc || !xmlDoc.documentElement) { - alert(_("xmlerror")+': Null document'); - return false; - } - this.owner.fromXML(xmlDoc.documentElement); - this.owner.window.close(); - return true; -} - -SQL.IO.prototype.clientsave = function() { - var xml = this.owner.toXML(); - this.dom.ta.value = xml; -} - -SQL.IO.prototype.clientload = function() { - var xml = this.dom.ta.value; - if (!xml) { - alert(_("empty")); - return; - } - - this.fromXMLText(xml); -} - -SQL.IO.prototype.promptName = function(title, suffix) { - var lastUsedName = this.owner.getOption("lastUsedName") || this.lastUsedName; - var name = prompt(_(title), lastUsedName); - if (!name) { return null; } - if (suffix && name.endsWith(suffix)) { - // remove suffix from name - name = name.substr(0, name.length-4); - } - this.owner.setOption("lastUsedName", name); - this.lastUsedName = name; // save this also in variable in case cookies are disabled - return name; -} - -SQL.IO.prototype.clientlocalsave = function() { - if (!window.localStorage) { - alert("Sorry, your browser does not seem to support localStorage."); - return; - } - - var xml = this.owner.toXML(); - if (xml.length >= (5*1024*1024)/2) { /* this is a very big db structure... */ - alert("Warning: your database structure is above 5 megabytes in size, this is above the localStorage single key limit allowed by some browsers, example Mozilla Firefox 10"); - return; - } - - var key = this.promptName("serversaveprompt"); - if (!key) { return; } - key = "wwwsqldesigner_databases_" + (key || "default"); - - try { - localStorage.setItem(key, xml); - if (localStorage.getItem(key) != xml) { throw new Error("Content verification failed"); } - } catch (e) { - alert("Error saving database structure to localStorage! ("+e.message+")"); - } -} - -SQL.IO.prototype.clientlocalload = function() { - if (!window.localStorage) { - alert("Sorry, your browser does not seem to support localStorage."); - return; - } - - var key = this.promptName("serverloadprompt"); - key = "wwwsqldesigner_databases_" + (key || "default"); - - try { - var xml = localStorage.getItem(key); - if (!xml) { throw new Error("No data available"); } - } catch (e) { - alert("Error loading database structure from localStorage! ("+e.message+")"); - return; - } - - this.fromXMLText(xml); -} - -SQL.IO.prototype.clientlocallist = function() { - if (!window.localStorage) { - alert("Sorry, your browser does not seem to support localStorage."); - return; - } - - /* --- Define some useful vars --- */ - var baseKeysName = "wwwsqldesigner_databases_"; - var localLen = localStorage.length; - var data = ""; - var schemasFound = false; - var code = 200; - - /* --- work --- */ - try { - for (var i = 0; i< localLen; ++i) { - var key = localStorage.key(i); - if((new RegExp(baseKeysName)).test(key)) { - var result = key.substring(baseKeysName.length); - schemasFound = true; - data += result + "\n"; - } - } - if (!schemasFound) { - throw new Error("No data available"); - } - } catch (e) { - alert("Error loading database names from localStorage! ("+e.message+")"); - return; - } - this.listresponse(data, code); -} - -/* ------------------------- Dropbox start ------------------------ */ - -/* - * The following code uses this lib: https://github.com/dropbox/dropbox-js - */ - -SQL.IO.prototype.dropBoxInit = function() -{ - if (dropboxAppKey) { - this.dropboxClient = new Dropbox.Client({ key: dropboxAppKey }); -} else { - this.dropboxClient = null; - - // Hide the Dropbox buttons and divider - var elems = document.querySelectorAll("[id^=dropbox]"); // gets all tags whose id start with "dropbox" - [].slice.call(elems).forEach( - function(elem) { elem.style.display = "none"; } - ); - } -} - -SQL.IO.prototype.showDropboxError = function(error) { - var prefix = _("Dropbox error")+": "; - var msg = error.status; - - switch (error.status) { - case Dropbox.ApiError.INVALID_TOKEN: - // If you're using dropbox.js, the only cause behind this error is that - // the user token expired. - // Get the user through the authentication flow again. - msg = _("Invalid Token (expired)"); - break; - - case Dropbox.ApiError.NOT_FOUND: - // The file or folder you tried to access is not in the user's Dropbox. - // Handling this error is specific to your application. - msg = _("File not found"); - break; - - case Dropbox.ApiError.OVER_QUOTA: - // The user is over their Dropbox quota. - // Tell them their Dropbox is full. Refreshing the page won't help. - msg = _("Dropbox is full"); - break; - - case Dropbox.ApiError.RATE_LIMITED: - // Too many API requests. Tell the user to try again later. - // Long-term, optimize your code to use fewer API calls. - break; - - case Dropbox.ApiError.NETWORK_ERROR: - // An error occurred at the XMLHttpRequest layer. - // Most likely, the user's network connection is down. - // API calls will not succeed until the user gets back online. - msg = _("Network error"); - break; - - case Dropbox.ApiError.INVALID_PARAM: - case Dropbox.ApiError.OAUTH_ERROR: - case Dropbox.ApiError.INVALID_METHOD: - default: - // Caused by a bug in dropbox.js, in your application, or in Dropbox. - // Tell the user an error occurred, ask them to refresh the page. - } - - alert (prefix+msg); -}; - -SQL.IO.prototype.showDropboxAuthenticate = function() { - if (!this.dropboxClient) return false; - - // We want to use a popup window for authentication as the default redirection won't work for us as it'll make us lose our schema data - this.dropboxClient.authDriver(new Dropbox.AuthDriver.Popup({ receiverUrl: "dropbox-oauth-receiver.html" })); - - // Now let's authenticate us - var success = false; - this.dropboxClient.authenticate( function(error, client) { - if (error) { - this.showDropboxError(error); - return; - } - success = true; - return; - }); - return success; -} - -SQL.IO.prototype.dropboxsave = function() { - if (!this.showDropboxAuthenticate()) return; - - var key = this.promptName("serversaveprompt", ".xml"); - if (!key) { return; } - var filename = (key || "default") + ".xml"; - - var sql_io = this; - sql_io.listresponse("Saving...", 200); - var xml = this.owner.toXML(); - this.dropboxClient.writeFile(filename, xml, function(error, stat) { - if (error) { - sql_io.listresponse("", 200); - return this.showDropboxError(error); - } - sql_io.listresponse(filename+" "+_("was saved to Dropbox"), 200); - }); -} - -SQL.IO.prototype.dropboxload = function() { - if (!this.showDropboxAuthenticate()) return; - - var key = this.promptName("serverloadprompt", ".xml"); - if (!key) { return; } - var filename = (key || "default") + ".xml"; - - var sql_io = this; - sql_io.listresponse("Loading...", 200); - this.dropboxClient.readFile(filename, function(error, data) { - sql_io.listresponse("", 200); - if (error) { - return this.showDropboxError(error); - } - sql_io.fromXMLText(data); - }); -} - -SQL.IO.prototype.dropboxlist = function() { - if (!this.showDropboxAuthenticate()) return; - - var sql_io = this; - sql_io.listresponse("Loading...", 200); - this.dropboxClient.readdir("/", function(error, entries) { - if (error) { - sql_io.listresponse("", 200); - return this.showDropboxError(error); - } - var data = entries.join("\n")+"\n"; - sql_io.listresponse(data, 200); - }); -} - - -/* ------------------------- Dropbox end ------------------------ */ - -SQL.IO.prototype.clientsql = function() { - var bp = this.owner.getOption("staticpath"); - var path = bp + "db/"+window.DATATYPES.getAttribute("db")+"/output.xsl"; - this.owner.window.showThrobber(); - OZ.Request(path, this.bind(this.finish), {xml:true}); -} - -SQL.IO.prototype.finish = function(xslDoc) { - this.owner.window.hideThrobber(); - var xml = this.owner.toXML(); - var sql = ""; - try { - if (window.XSLTProcessor && window.DOMParser) { - var parser = new DOMParser(); - var xmlDoc = parser.parseFromString(xml, "text/xml"); - var xsl = new XSLTProcessor(); - xsl.importStylesheet(xslDoc); - var result = xsl.transformToDocument(xmlDoc); - sql = result.documentElement.textContent; - } else if (window.ActiveXObject || "ActiveXObject" in window) { - var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); - xmlDoc.loadXML(xml); - sql = xmlDoc.transformNode(xslDoc); - } else { - throw new Error("No XSLT processor available"); - } - } catch(e) { - alert(_("xmlerror")+': '+e.message); - return; - } - this.dom.ta.value = sql.trim(); -} - -SQL.IO.prototype.serversave = function(e, keyword) { - var name = keyword || prompt(_("serversaveprompt"), this._name); - if (!name) { return; } - this._name = name; - var xml = this.owner.toXML(); - var bp = this.owner.getOption("xhrpath"); - var url = bp + "backend/"+this.dom.backend.value+"/?action=save&keyword="+encodeURIComponent(name); - var h = {"Content-type":"application/xml"}; - this.owner.window.showThrobber(); - this.owner.setTitle(name); - OZ.Request(url, this.saveresponse, {xml:true, method:"post", data:xml, headers:h}); -} - -SQL.IO.prototype.quicksave = function(e) { - this.serversave(e, this._name); -} - -SQL.IO.prototype.serverload = function(e, keyword) { - var name = keyword || prompt(_("serverloadprompt"), this._name); - if (!name) { return; } - this._name = name; - var bp = this.owner.getOption("xhrpath"); - var url = bp + "backend/"+this.dom.backend.value+"/?action=load&keyword="+encodeURIComponent(name); - this.owner.window.showThrobber(); - this.name = name; - OZ.Request(url, this.loadresponse, {xml:true}); -} - -SQL.IO.prototype.serverlist = function(e) { - var bp = this.owner.getOption("xhrpath"); - var url = bp + "backend/"+this.dom.backend.value+"/?action=list"; - this.owner.window.showThrobber(); - OZ.Request(url, this.listresponse); -} - -SQL.IO.prototype.serverimport = function(e) { - var name = prompt(_("serverimportprompt"), ""); - if (!name) { return; } - var bp = this.owner.getOption("xhrpath"); - var url = bp + "backend/"+this.dom.backend.value+"/?action=import&database="+name; - this.owner.window.showThrobber(); - OZ.Request(url, this.importresponse, {xml:true}); -} - -SQL.IO.prototype.check = function(code) { - switch (code) { - case 201: - case 404: - case 500: - case 501: - case 503: - var lang = "http"+code; - this.dom.ta.value = _("httpresponse")+": "+_(lang); - return false; - break; - default: return true; - } -} - -SQL.IO.prototype.saveresponse = function(data, code) { - this.owner.window.hideThrobber(); - this.check(code); -} - -SQL.IO.prototype.loadresponse = function(data, code) { - this.owner.window.hideThrobber(); - if (!this.check(code)) { return; } - this.fromXML(data); - this.owner.setTitle(this.name); -} - -SQL.IO.prototype.listresponse = function(data, code) { - this.owner.window.hideThrobber(); - if (!this.check(code)) { return; } - this.dom.ta.value = data; -} - -SQL.IO.prototype.importresponse = function(data, code) { - this.owner.window.hideThrobber(); - if (!this.check(code)) { return; } - if (this.fromXML(data)) { - this.owner.alignTables(); - } -} - -SQL.IO.prototype.press = function(e) { - switch (e.keyCode) { - case 113: - if (OZ.opera) { - e.preventDefault(); - } - this.quicksave(e); - break; - } -} - -/* --------------------- table manager ------------ */ - -SQL.TableManager = OZ.Class(); - -SQL.TableManager.prototype.init = function(owner) { - this.owner = owner; - this.dom = { - container:OZ.$("table"), - name:OZ.$("tablename"), - comment:OZ.$("tablecomment") - }; - this.selection = []; - this.adding = false; - - var ids = ["addtable","removetable","aligntables","cleartables","addrow","edittable","tablekeys"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - this.dom[id] = elm; - elm.value = _(id); - } - - var ids = ["tablenamelabel","tablecommentlabel"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - elm.innerHTML = _(id); - } - - - this.select(false); - - this.save = this.bind(this.save); - - OZ.Event.add("area", "click", this.bind(this.click)); - OZ.Event.add(this.dom.addtable, "click", this.bind(this.preAdd)); - OZ.Event.add(this.dom.removetable, "click", this.bind(this.remove)); - OZ.Event.add(this.dom.cleartables, "click", this.bind(this.clear)); - OZ.Event.add(this.dom.addrow, "click", this.bind(this.addRow)); - OZ.Event.add(this.dom.aligntables, "click", this.owner.bind(this.owner.alignTables)); - OZ.Event.add(this.dom.edittable, "click", this.bind(this.edit)); - OZ.Event.add(this.dom.tablekeys, "click", this.bind(this.keys)); - OZ.Event.add(document, "keydown", this.bind(this.press)); - - this.dom.container.parentNode.removeChild(this.dom.container); -} - -SQL.TableManager.prototype.addRow = function(e) { - var newrow = this.selection[0].addRow(_("newrow")); - this.owner.rowManager.select(newrow); - newrow.expand(); -} - -SQL.TableManager.prototype.select = function(table, multi) { /* activate table */ - if (table) { - if (multi) { - var i = this.selection.indexOf(table); - if (i < 0) { - this.selection.push(table); - } else { - this.selection.splice(i, 1); - } - } else { - if (this.selection[0] === table) { return; } - this.selection = [table]; - } - } else { - this.selection = []; - } - this.processSelection(); -} - -SQL.TableManager.prototype.processSelection = function() { - var tables = this.owner.tables; - for (var i=0;i<tables.length;i++) { - tables[i].deselect(); - } - if (this.selection.length == 1) { - this.dom.addrow.disabled = false; - this.dom.edittable.disabled = false; - this.dom.tablekeys.disabled = false; - this.dom.removetable.value = _("removetable"); - } else { - this.dom.addrow.disabled = true; - this.dom.edittable.disabled = true; - this.dom.tablekeys.disabled = true; - } - if (this.selection.length) { - this.dom.removetable.disabled = false; - if (this.selection.length > 1) { this.dom.removetable.value = _("removetables"); } - } else { - this.dom.removetable.disabled = true; - this.dom.removetable.value = _("removetable"); - } - for (var i=0;i<this.selection.length;i++) { - var t = this.selection[i]; - t.owner.raise(t); - t.select(); - } -} - -SQL.TableManager.prototype.selectRect = function(x,y,width,height) { /* select all tables intersecting a rectangle */ - this.selection = []; - var tables = this.owner.tables; - var x1 = x+width; - var y1 = y+height; - for (var i=0;i<tables.length;i++) { - var t = tables[i]; - var tx = t.x; - var tx1 = t.x+t.width; - var ty = t.y; - var ty1 = t.y+t.height; - if (((tx>=x && tx<x1) || (tx1>=x && tx1<x1) || (tx<x && tx1>x1)) && - ((ty>=y && ty<y1) || (ty1>=y && ty1<y1) || (ty<y && ty1>y1))) - { this.selection.push(t); } - } - this.processSelection(); -} - -SQL.TableManager.prototype.click = function(e) { /* finish adding new table */ - var newtable = false; - if (this.adding) { - this.adding = false; - OZ.DOM.removeClass("area","adding"); - this.dom.addtable.value = this.oldvalue; - var scroll = OZ.DOM.scroll(); - var x = e.clientX + scroll[0]; - var y = e.clientY + scroll[1]; - newtable = this.owner.addTable(_("newtable"),x,y); - var r = newtable.addRow("id",{ai:true}); - var k = newtable.addKey("PRIMARY",""); - k.addRow(r); - } - this.select(newtable); - this.owner.rowManager.select(false); - if (this.selection.length == 1) { this.edit(e); } -} - -SQL.TableManager.prototype.preAdd = function(e) { /* click add new table */ - if (this.adding) { - this.adding = false; - OZ.DOM.removeClass("area","adding"); - this.dom.addtable.value = this.oldvalue; - } else { - this.adding = true; - OZ.DOM.addClass("area","adding"); - this.oldvalue = this.dom.addtable.value; - this.dom.addtable.value = "["+_("addpending")+"]"; - } -} - -SQL.TableManager.prototype.clear = function(e) { /* remove all tables */ - if (!this.owner.tables.length) { return; } - var result = confirm(_("confirmall")+" ?"); - if (!result) { return; } - this.owner.clearTables(); -} - -SQL.TableManager.prototype.remove = function(e) { - var titles = this.selection.slice(0); - for (var i=0;i<titles.length;i++) { titles[i] = "'"+titles[i].getTitle()+"'"; } - var result = confirm(_("confirmtable")+" "+titles.join(", ")+"?"); - if (!result) { return; } - var sel = this.selection.slice(0); - for (var i=0;i<sel.length;i++) { this.owner.removeTable(sel[i]); } -} - -SQL.TableManager.prototype.edit = function(e) { - this.owner.window.open(_("edittable"), this.dom.container, this.save); - - var title = this.selection[0].getTitle(); - this.dom.name.value = title; - try { /* throws in ie6 */ - this.dom.comment.value = this.selection[0].getComment(); - } catch(e) {} - - /* pre-select table name */ - this.dom.name.focus(); - if (OZ.ie) { - try { /* throws in ie6 */ - this.dom.name.select(); - } catch(e) {} - } else { - this.dom.name.setSelectionRange(0, title.length); - } -} - -SQL.TableManager.prototype.keys = function(e) { /* open keys dialog */ - this.owner.keyManager.open(this.selection[0]); -} - -SQL.TableManager.prototype.save = function() { - this.selection[0].setTitle(this.dom.name.value); - this.selection[0].setComment(this.dom.comment.value); -} - -SQL.TableManager.prototype.press = function(e) { - var target = OZ.Event.target(e).nodeName.toLowerCase(); - if (target == "textarea" || target == "input") { return; } /* not when in form field */ - - if (this.owner.rowManager.selected) { return; } /* do not process keypresses if a row is selected */ - - if (!this.selection.length) { return; } /* nothing if selection is active */ - - switch (e.keyCode) { - case 46: - this.remove(); - OZ.Event.prevent(e); - break; - } -} - -/* --------------------- row manager ------------ */ - -SQL.RowManager = OZ.Class(); - -SQL.RowManager.prototype.init = function(owner) { - this.owner = owner; - this.dom = {}; - this.selected = null; - this.creating = false; - this.connecting = false; - - var ids = ["editrow","removerow","uprow","downrow","foreigncreate","foreignconnect","foreigndisconnect"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - this.dom[id] = elm; - elm.value = _(id); - } - - this.select(false); - - OZ.Event.add(this.dom.editrow, "click", this.bind(this.edit)); - OZ.Event.add(this.dom.uprow, "click", this.bind(this.up)); - OZ.Event.add(this.dom.downrow, "click", this.bind(this.down)); - OZ.Event.add(this.dom.removerow, "click", this.bind(this.remove)); - OZ.Event.add(this.dom.foreigncreate, "click", this.bind(this.foreigncreate)); - OZ.Event.add(this.dom.foreignconnect, "click", this.bind(this.foreignconnect)); - OZ.Event.add(this.dom.foreigndisconnect, "click", this.bind(this.foreigndisconnect)); - OZ.Event.add(false, "tableclick", this.bind(this.tableClick)); - OZ.Event.add(false, "rowclick", this.bind(this.rowClick)); - OZ.Event.add(document, "keydown", this.bind(this.press)); -} - -SQL.RowManager.prototype.select = function(row) { /* activate a row */ - if (this.selected === row) { return; } - if (this.selected) { this.selected.deselect(); } - - this.selected = row; - if (this.selected) { this.selected.select(); } - this.redraw(); -} - -SQL.RowManager.prototype.tableClick = function(e) { /* create relation after clicking target table */ - if (!this.creating) { return; } - - var r1 = this.selected; - var t2 = e.target; - - var p = this.owner.getOption("pattern"); - p = p.replace(/%T/g,r1.owner.getTitle()); - p = p.replace(/%t/g,t2.getTitle()); - p = p.replace(/%R/g,r1.getTitle()); - - var r2 = t2.addRow(p, r1.data); - r2.update({"type":SQL.Designer.getFKTypeFor(r1.data.type)}); - r2.update({"ai":false}); - this.owner.addRelation(r1, r2); -} - -SQL.RowManager.prototype.rowClick = function(e) { /* draw relation after clicking target row */ - if (!this.connecting) { return; } - - var r1 = this.selected; - var r2 = e.target; - - if (r1 == r2) { return; } - - this.owner.addRelation(r1, r2); -} - -SQL.RowManager.prototype.foreigncreate = function(e) { /* start creating fk */ - this.endConnect(); - if (this.creating) { - this.endCreate(); - } else { - this.creating = true; - this.dom.foreigncreate.value = "["+_("foreignpending")+"]"; - } -} - -SQL.RowManager.prototype.foreignconnect = function(e) { /* start drawing fk */ - this.endCreate(); - if (this.connecting) { - this.endConnect(); - } else { - this.connecting = true; - this.dom.foreignconnect.value = "["+_("foreignconnectpending")+"]"; - } -} - -SQL.RowManager.prototype.foreigndisconnect = function(e) { /* remove connector */ - var rels = this.selected.relations; - for (var i=rels.length-1;i>=0;i--) { - var r = rels[i]; - if (r.row2 == this.selected) { this.owner.removeRelation(r); } - } - this.redraw(); -} - -SQL.RowManager.prototype.endCreate = function() { - this.creating = false; - this.dom.foreigncreate.value = _("foreigncreate"); -} - -SQL.RowManager.prototype.endConnect = function() { - this.connecting = false; - this.dom.foreignconnect.value = _("foreignconnect"); -} - -SQL.RowManager.prototype.up = function(e) { - this.selected.up(); - this.redraw(); -} - -SQL.RowManager.prototype.down = function(e) { - this.selected.down(); - this.redraw(); -} - -SQL.RowManager.prototype.remove = function(e) { - var result = confirm(_("confirmrow")+" '"+this.selected.getTitle()+"' ?"); - if (!result) { return; } - var t = this.selected.owner; - this.selected.owner.removeRow(this.selected); - - var next = false; - if (t.rows) { next = t.rows[t.rows.length-1]; } - this.select(next); -} - -SQL.RowManager.prototype.redraw = function() { - this.endCreate(); - this.endConnect(); - if (this.selected) { - var table = this.selected.owner; - var rows = table.rows; - this.dom.uprow.disabled = (rows[0] == this.selected); - this.dom.downrow.disabled = (rows[rows.length-1] == this.selected); - this.dom.removerow.disabled = false; - this.dom.editrow.disabled = false; - this.dom.foreigncreate.disabled = !(this.selected.isUnique()); - this.dom.foreignconnect.disabled = !(this.selected.isUnique()); - - this.dom.foreigndisconnect.disabled = true; - var rels = this.selected.relations; - for (var i=0;i<rels.length;i++) { - var r = rels[i]; - if (r.row2 == this.selected) { this.dom.foreigndisconnect.disabled = false; } - } - - } else { - this.dom.uprow.disabled = true; - this.dom.downrow.disabled = true; - this.dom.removerow.disabled = true; - this.dom.editrow.disabled = true; - this.dom.foreigncreate.disabled = true; - this.dom.foreignconnect.disabled = true; - this.dom.foreigndisconnect.disabled = true; - } -} - -SQL.RowManager.prototype.press = function(e) { - if (!this.selected) { return; } - - var target = OZ.Event.target(e).nodeName.toLowerCase(); - if (target == "textarea" || target == "input") { return; } /* not when in form field */ - - switch (e.keyCode) { - case 38: - this.up(); - OZ.Event.prevent(e); - break; - case 40: - this.down(); - OZ.Event.prevent(e); - break; - case 46: - this.remove(); - OZ.Event.prevent(e); - break; - case 13: - case 27: - this.selected.collapse(); - break; - } -} - -SQL.RowManager.prototype.edit = function(e) { - this.selected.expand(); -} - -/* ----------------- key manager ---------- */ - -SQL.KeyManager = OZ.Class(); - -SQL.KeyManager.prototype.init = function(owner) { - this.owner = owner; - this.dom = { - container:OZ.$("keys") - } - this.build(); -} - -SQL.KeyManager.prototype.build = function() { - this.dom.list = OZ.$("keyslist"); - this.dom.type = OZ.$("keytype"); - this.dom.name = OZ.$("keyname"); - this.dom.left = OZ.$("keyleft"); - this.dom.right = OZ.$("keyright"); - this.dom.fields = OZ.$("keyfields"); - this.dom.avail = OZ.$("keyavail"); - this.dom.listlabel = OZ.$("keyslistlabel"); - - var ids = ["keyadd","keyremove"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - this.dom[id] = elm; - elm.value = _(id); - } - - var ids = ["keyedit","keytypelabel","keynamelabel","keyfieldslabel","keyavaillabel"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - elm.innerHTML = _(id); - } - - var types = ["PRIMARY","INDEX","UNIQUE","FULLTEXT"]; - OZ.DOM.clear(this.dom.type); - for (var i=0;i<types.length;i++) { - var o = OZ.DOM.elm("option"); - o.innerHTML = types[i]; - o.value = types[i]; - this.dom.type.appendChild(o); - } - - this.purge = this.bind(this.purge); - - OZ.Event.add(this.dom.list, "change", this.bind(this.listchange)); - OZ.Event.add(this.dom.type, "change", this.bind(this.typechange)); - OZ.Event.add(this.dom.name, "keyup", this.bind(this.namechange)); - OZ.Event.add(this.dom.keyadd, "click", this.bind(this.add)); - OZ.Event.add(this.dom.keyremove, "click", this.bind(this.remove)); - OZ.Event.add(this.dom.left, "click", this.bind(this.left)); - OZ.Event.add(this.dom.right, "click", this.bind(this.right)); - - this.dom.container.parentNode.removeChild(this.dom.container); -} - -SQL.KeyManager.prototype.listchange = function(e) { - this.switchTo(this.dom.list.selectedIndex); -} - -SQL.KeyManager.prototype.typechange = function(e) { - this.key.setType(this.dom.type.value); - this.redrawListItem(); -} - -SQL.KeyManager.prototype.namechange = function(e) { - this.key.setName(this.dom.name.value); - this.redrawListItem(); -} - -SQL.KeyManager.prototype.add = function(e) { - var type = (this.table.keys.length ? "INDEX" : "PRIMARY"); - this.table.addKey(type); - this.sync(this.table); - this.switchTo(this.table.keys.length-1); -} - -SQL.KeyManager.prototype.remove = function(e) { - var index = this.dom.list.selectedIndex; - if (index == -1) { return; } - var r = this.table.keys[index]; - this.table.removeKey(r); - this.sync(this.table); -} - -SQL.KeyManager.prototype.purge = function() { /* remove empty keys */ - for (var i=this.table.keys.length-1;i>=0;i--) { - var k = this.table.keys[i]; - if (!k.rows.length) { this.table.removeKey(k); } - } -} - -SQL.KeyManager.prototype.sync = function(table) { /* sync content with given table */ - this.table = table; - this.dom.listlabel.innerHTML = _("keyslistlabel").replace(/%s/,table.getTitle()); - - OZ.DOM.clear(this.dom.list); - for (var i=0;i<table.keys.length;i++) { - var k = table.keys[i]; - var o = OZ.DOM.elm("option"); - this.dom.list.appendChild(o); - var str = (i+1)+": "+k.getLabel(); - o.innerHTML = str; - } - if (table.keys.length) { - this.switchTo(0); - } else { - this.disable(); - } -} - -SQL.KeyManager.prototype.redrawListItem = function() { - var index = this.table.keys.indexOf(this.key); - this.option.innerHTML = (index+1)+": "+this.key.getLabel(); -} - -SQL.KeyManager.prototype.switchTo = function(index) { /* show Nth key */ - this.enable(); - var k = this.table.keys[index]; - this.key = k; - this.option = this.dom.list.getElementsByTagName("option")[index]; - - this.dom.list.selectedIndex = index; - this.dom.name.value = k.getName(); - - var opts = this.dom.type.getElementsByTagName("option"); - for (var i=0;i<opts.length;i++) { - if (opts[i].value == k.getType()) { this.dom.type.selectedIndex = i; } - } - - OZ.DOM.clear(this.dom.fields); - for (var i=0;i<k.rows.length;i++) { - var o = OZ.DOM.elm("option"); - o.innerHTML = k.rows[i].getTitle(); - o.value = o.innerHTML; - this.dom.fields.appendChild(o); - } - - OZ.DOM.clear(this.dom.avail); - for (var i=0;i<this.table.rows.length;i++) { - var r = this.table.rows[i]; - if (k.rows.indexOf(r) != -1) { continue; } - var o = OZ.DOM.elm("option"); - o.innerHTML = r.getTitle(); - o.value = o.innerHTML; - this.dom.avail.appendChild(o); - } -} - -SQL.KeyManager.prototype.disable = function() { - OZ.DOM.clear(this.dom.fields); - OZ.DOM.clear(this.dom.avail); - this.dom.keyremove.disabled = true; - this.dom.left.disabled = true; - this.dom.right.disabled = true; - this.dom.list.disabled = true; - this.dom.name.disabled = true; - this.dom.type.disabled = true; - this.dom.fields.disabled = true; - this.dom.avail.disabled = true; -} - -SQL.KeyManager.prototype.enable = function() { - this.dom.keyremove.disabled = false; - this.dom.left.disabled = false; - this.dom.right.disabled = false; - this.dom.list.disabled = false; - this.dom.name.disabled = false; - this.dom.type.disabled = false; - this.dom.fields.disabled = false; - this.dom.avail.disabled = false; -} - -SQL.KeyManager.prototype.left = function(e) { /* add field to index */ - var opts = this.dom.avail.getElementsByTagName("option"); - for (var i=0;i<opts.length;i++) { - var o = opts[i]; - if (o.selected) { - var row = this.table.findNamedRow(o.value); - this.key.addRow(row); - } - } - this.switchTo(this.dom.list.selectedIndex); -} - -SQL.KeyManager.prototype.right = function(e) { /* remove field from index */ - var opts = this.dom.fields.getElementsByTagName("option"); - for (var i=0;i<opts.length;i++) { - var o = opts[i]; - if (o.selected) { - var row = this.table.findNamedRow(o.value); - this.key.removeRow(row); - } - } - this.switchTo(this.dom.list.selectedIndex); -} - -SQL.KeyManager.prototype.open = function(table) { - this.sync(table); - this.owner.window.open(_("tablekeys"),this.dom.container,this.purge); -} - -/* --------------------- window ------------ */ - -SQL.Window = OZ.Class(); - -SQL.Window.prototype.init = function(owner) { - this.owner = owner; - this.dom = { - container:OZ.$("window"), - background:OZ.$("background"), - ok:OZ.$("windowok"), - cancel:OZ.$("windowcancel"), - title:OZ.$("windowtitle"), - content:OZ.$("windowcontent"), - throbber:OZ.$("throbber") - } - this.dom.ok.value = _("windowok"); - this.dom.cancel.value = _("windowcancel"); - this.dom.throbber.alt = this.dom.throbber.title = _("throbber"); - OZ.Event.add(this.dom.ok, "click", this.bind(this.ok)); - OZ.Event.add(this.dom.cancel, "click", this.bind(this.close)); - OZ.Event.add(document, "keydown", this.bind(this.key)); - - this.sync = this.bind(this.sync); - - OZ.Event.add(window, "scroll", this.sync); - OZ.Event.add(window, "resize", this.sync); - this.state = 0; - this.hideThrobber(); - - this.sync(); -} - -SQL.Window.prototype.showThrobber = function() { - this.dom.throbber.style.visibility = ""; -} - -SQL.Window.prototype.hideThrobber = function() { - this.dom.throbber.style.visibility = "hidden"; -} - -SQL.Window.prototype.open = function(title, content, callback) { - this.state = 1; - this.callback = callback; - while (this.dom.title.childNodes.length > 1) { this.dom.title.removeChild(this.dom.title.childNodes[1]); } - - var txt = OZ.DOM.text(title); - this.dom.title.appendChild(txt); - this.dom.background.style.visibility = "visible"; - OZ.DOM.clear(this.dom.content); - this.dom.content.appendChild(content); - - var win = OZ.DOM.win(); - var scroll = OZ.DOM.scroll(); - this.dom.container.style.left = Math.round(scroll[0] + (win[0] - this.dom.container.offsetWidth)/2)+"px"; - this.dom.container.style.top = Math.round(scroll[1] + (win[1] - this.dom.container.offsetHeight)/2)+"px"; - - this.dom.cancel.style.visibility = (this.callback ? "" : "hidden"); - this.dom.container.style.visibility = "visible"; - - var formElements = ["input","select","textarea"]; - var all = this.dom.container.getElementsByTagName("*"); - for (var i=0;i<all.length;i++) { - if (formElements.indexOf(all[i].tagName.toLowerCase()) != -1) { - all[i].focus(); - break; - } - } -} - -SQL.Window.prototype.key = function(e) { - if (!this.state) { return; } - if (e.keyCode == 13) { this.ok(e); } - if (e.keyCode == 27) { this.close(); } -} - -SQL.Window.prototype.ok = function(e) { - if (this.callback) { this.callback(); } - this.close(); -} - -SQL.Window.prototype.close = function() { - if (!this.state) { return; } - this.state = 0; - this.dom.background.style.visibility = "hidden"; - this.dom.container.style.visibility = "hidden"; -} - -SQL.Window.prototype.sync = function() { /* adjust background position */ - var dims = OZ.DOM.win(); - var scroll = OZ.DOM.scroll(); - this.dom.background.style.width = dims[0]+"px"; - this.dom.background.style.height = dims[1]+"px"; - this.dom.background.style.left = scroll[0]+"px"; - this.dom.background.style.top = scroll[1]+"px"; -} - -/* --------------------- options ------------ */ - -SQL.Options = OZ.Class(); - -SQL.Options.prototype.init = function(owner) { - this.owner = owner; - this.dom = { - container:OZ.$("opts"), - btn:OZ.$("options") - } - this.dom.btn.value = _("options"); - this.save = this.bind(this.save); - this.build(); -} - -SQL.Options.prototype.build = function() { - this.dom.optionlocale = OZ.$("optionlocale"); - this.dom.optiondb = OZ.$("optiondb"); - this.dom.optionsnap = OZ.$("optionsnap"); - this.dom.optionpattern = OZ.$("optionpattern"); - this.dom.optionhide = OZ.$("optionhide"); - this.dom.optionvector = OZ.$("optionvector"); - this.dom.optionshowsize = OZ.$("optionshowsize"); - this.dom.optionshowtype = OZ.$("optionshowtype"); - - var ids = ["language","db","snap","pattern","hide","vector","showsize","showtype","optionsnapnotice","optionpatternnotice","optionsnotice"]; - for (var i=0;i<ids.length;i++) { - var id = ids[i]; - var elm = OZ.$(id); - elm.innerHTML = _(id); - } - - var ls = CONFIG.AVAILABLE_LOCALES; - OZ.DOM.clear(this.dom.optionlocale); - for (var i=0;i<ls.length;i++) { - var o = OZ.DOM.elm("option"); - o.value = ls[i]; - o.innerHTML = ls[i]; - this.dom.optionlocale.appendChild(o); - if (this.owner.getOption("locale") == ls[i]) { this.dom.optionlocale.selectedIndex = i; } - } - - var dbs = CONFIG.AVAILABLE_DBS; - OZ.DOM.clear(this.dom.optiondb); - for (var i=0;i<dbs.length;i++) { - var o = OZ.DOM.elm("option"); - o.value = dbs[i]; - o.innerHTML = dbs[i]; - this.dom.optiondb.appendChild(o); - if (this.owner.getOption("db") == dbs[i]) { this.dom.optiondb.selectedIndex = i; } - } - - - OZ.Event.add(this.dom.btn, "click", this.bind(this.click)); - - this.dom.container.parentNode.removeChild(this.dom.container); -} - -SQL.Options.prototype.save = function() { - this.owner.setOption("locale",this.dom.optionlocale.value); - this.owner.setOption("db",this.dom.optiondb.value); - this.owner.setOption("snap",this.dom.optionsnap.value); - this.owner.setOption("pattern",this.dom.optionpattern.value); - this.owner.setOption("hide",this.dom.optionhide.checked ? "1" : ""); - this.owner.setOption("vector",this.dom.optionvector.checked ? "1" : ""); - this.owner.setOption("showsize",this.dom.optionshowsize.checked ? "1" : ""); - this.owner.setOption("showtype",this.dom.optionshowtype.checked ? "1" : ""); -} - -SQL.Options.prototype.click = function() { - this.owner.window.open(_("options"),this.dom.container,this.save); - this.dom.optionsnap.value = this.owner.getOption("snap"); - this.dom.optionpattern.value = this.owner.getOption("pattern"); - this.dom.optionhide.checked = this.owner.getOption("hide"); - this.dom.optionvector.checked = this.owner.getOption("vector"); - this.dom.optionshowsize.checked = this.owner.getOption("showsize"); - this.dom.optionshowtype.checked = this.owner.getOption("showtype"); -} - -/* ------------------ minimize/restore bar ----------- */ - -SQL.Toggle = OZ.Class(); - -SQL.Toggle.prototype.init = function(elm) { - this._state = null; - this._elm = elm; - OZ.Event.add(elm, "click", this._click.bind(this)); - - var defaultState = true; - if (document.location.href.match(/toolbar=hidden/)) { defaultState = false; } - this._switch(defaultState); -} - -SQL.Toggle.prototype._click = function(e) { - this._switch(!this._state); -} - -SQL.Toggle.prototype._switch = function(state) { - this._state = state; - if (this._state) { - OZ.$("bar").style.height = ""; - } else { - OZ.$("bar").style.overflow = "hidden"; - OZ.$("bar").style.height = this._elm.offsetHeight + "px"; - } - this._elm.className = (this._state ? "on" : "off"); -} - /* --------------------- www sql designer ------------ */ SQL.Designer = OZ.Class().extend(SQL.Visual); |