122 | Select a schema and then add tables/views. 123 |124 |
├── .gitignore ├── LICENSE ├── ajax ├── table_json.php └── table_list.php ├── coffee ├── bool-expr.coffee ├── bool-expr.js ├── core.coffee ├── core.js ├── core.panes.coffee ├── core.panes.js ├── design.coffee ├── design.js ├── save.js ├── sql2diag.js └── watch.sh ├── conn.php ├── css ├── design.css └── tbl_list_overlay.css ├── images └── icons │ └── cross.png ├── index.php ├── js ├── backbone-localstorage.js ├── backbone-min.js ├── jquery-ui.js ├── jquery.cookie.js ├── jquery.min.js ├── jsplumb.js ├── tbl-selection.js └── underscore-min.js ├── lib.php ├── readme.md └── table_sel_list.php /.gitignore: -------------------------------------------------------------------------------- 1 | _notes/ 2 | _other/ 3 | *.komodoproject 4 | .komodotools 5 | *.zip 6 | *.7z 7 | /coffee/demo* 8 | *.bak 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2012 Swapnil M. Joshi 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /ajax/table_json.php: -------------------------------------------------------------------------------- 1 | $table_name, 19 | "ColumnName" => '*', 20 | "IsPrimaryKey" => FALSE 21 | ); 22 | while($row = mysqli_fetch_array($result)){ 23 | 24 | $FieldData[] = array( 25 | "TableName" => $table_name, 26 | "ColumnName" => $row['Field'], 27 | "IsPrimaryKey" => ($row['Key'] == 'PRI'? TRUE:FALSE) 28 | //"Selected" => true 29 | ); 30 | 31 | } 32 | 33 | /* 34 | $finalArray = array( 35 | "TableName"=> $table_name , 36 | "Fields" => $FieldData 37 | 38 | ); 39 | */ 40 | 41 | $finalArray = $FieldData; 42 | 43 | print json_encode($finalArray); 44 | 45 | ?> 46 | -------------------------------------------------------------------------------- /ajax/table_list.php: -------------------------------------------------------------------------------- 1 | 6 | 15 |
16 | -------------------------------------------------------------------------------- /coffee/bool-expr.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | 3 | window.BoolExpr = Backbone.Model.extend 4 | defaults: -> 5 | LeftTableField: null #'' # TableField 6 | RightOperand: '' #'' 7 | Operator: Constants.EQUALS 8 | isCompoundExpr: false 9 | deleteOK:false 10 | 11 | set_isCompoundExpr : -> 12 | # return true if RightOperand is of type TableField 13 | @set {isCompoundExpr : (@get('RightOperand') instanceof TableField)},{silent:true} 14 | 15 | toString: -> 16 | #"#{@get('LeftTableField')} #{@get('Operator')} #{@get('RightOperand')} " 17 | 18 | if @get 'isCompoundExpr' 19 | "(#{@get('LeftTableField').toString()} #{@get('Operator')} #{@get('RightOperand').toString()})" 20 | else 21 | #"#{@get('LeftTableField')} #{@get('Operator')} #{@get('RightOperand')} " 22 | op = @get 'Operator' 23 | rightOp = @get 'RightOperand' 24 | #enclose in single quotes if used LIKE or rightOp is a string 25 | #rightOp = "'" + rightOp + "'" if op== Constants.LIKE 26 | if isNaN(rightOp) or op== Constants.LIKE 27 | rightOp = "'" + rightOp + "'" 28 | "(#{@get('LeftTableField')} #{@get('Operator')} #{rightOp})" 29 | 30 | getLeftTableField: -> 31 | @get 'LeftTableField' 32 | 33 | getRightOperand: -> 34 | @get 'RightOperand' 35 | 36 | clear: -> 37 | #@destroy() 38 | #BoolExprs.remove @ 39 | @set deleteOK : true 40 | 41 | isCompound: -> 42 | return @get 'isCompoundExpr' 43 | 44 | setOperator: (newOp) -> 45 | @set Operator : newOp 46 | 47 | addBoolExpr: (boolExpr) -> 48 | ### 49 | Turns self into CompBoolExpr form BoolExpr 50 | LeftTableField = clone 51 | RightOperand = boolExpr 52 | Operator = AND 53 | 54 | ### 55 | 56 | console.log "Inside: addBoolExpr" 57 | 58 | #newLeftTF= @clone() #use BoolExprs.create ? 59 | newM = @toJSON() 60 | newLeftTF = BoolExprs.create { 61 | LeftTableField : newM.LeftTableField 62 | RightOperand: newM.RightOperand 63 | Operator: newM.Operator 64 | isCompoundExpr: newM.isCompoundExpr 65 | 66 | },{ silent:true} 67 | 68 | try 69 | @set { 70 | LeftTableField: newLeftTF 71 | RightOperand: boolExpr 72 | Operator: Constants.AND 73 | }, {silent:true} 74 | 75 | #,{silent:true} 76 | catch error 77 | console.log error 78 | 79 | @set {isCompoundExpr : true},{silent: true} 80 | @trigger() 81 | 82 | 83 | window.BoolExprView = Backbone.View.extend 84 | template: _.template($('#bool-expr-template').html()) 85 | events: 86 | "dragstart ": "setCachedView" 87 | "click .operator": "changeOperator" 88 | #"click .operator": "testHnd" 89 | "blur .right-operand" : "setRightOperand" 90 | "click .delete" : "clearIfLast" 91 | "click .bool.expr.left > .delete" : "clearLeftTableField" 92 | "click .bool.expr.right > .delete" : "clearRightTableField" 93 | 94 | testHnd:-> 95 | console.log "handled" 96 | 97 | setRightOperand:(evt) -> 98 | #set only if current view is NOT of CompBoolExpr 99 | #console.log 'arguments' 100 | #console.log arguments 101 | #if !@model.isCompound() 102 | #if $(@el).closest('.expr') == @el 103 | #handle event only if raised by direct child 104 | #if @.$('.right-operand').get(0) == evt.srcElement 105 | if $(@el).children('.right-operand').get(0) == evt.srcElement 106 | val = @.$('.right-operand').val() 107 | @model.set RightOperand: val 108 | 109 | className: "expr" 110 | initialize: -> 111 | #@model.bind('change:isCompoundExpr', @chgNode, @) 112 | #@model.bind('change', @render_spcl, this) 113 | @model.bind('change', @render, this) 114 | @model.bind('change:isCompoundExpr', @render, this) 115 | #@model.bind('change', @childExprRemoved, this) 116 | #@model.bind('remove', @clear, this) 117 | 118 | @model.bind('destroy', @remove, this) 119 | 120 | @leftView=null 121 | @rightView=null 122 | 123 | #clearBoolExpr: -> 124 | 125 | 126 | 127 | #clear: -> 128 | #if !@model.isCompound() 129 | #@model.clear() 130 | #@remove() 131 | 132 | #childExprRemoved: -> 133 | ##if both are in place NO OP 134 | #return 0 if @model.getLeftTableField() and @model.getRightOperand() 135 | ##not removed one 136 | ##liveExpr = if @model.getLeftTableField then 137 | ##liveExpr if @model.getLeftTableField? 138 | #liveExpr = @model.getLeftTableField() or @model.getRightOperand() 139 | ##if @model.getLeftTableField().get 'deleteOK' 140 | 141 | #@model.set { 142 | #LeftTableField : liveExpr.LeftTableField 143 | #Operator: liveExpr.Operator 144 | #RightOperand: liveExpr.RightOperand 145 | #isCompoundExpr: liveExpr.isCompoundExpr 146 | #},{silent:true} 147 | ##@model.set {isCompoundExpr : false},{silent:true} 148 | #@model.trigger() 149 | 150 | 151 | render: -> 152 | #$(@el).html(@template(@model.toJSON())) 153 | #@model.set_isCompoundExpr() 154 | if(@model.get('isCompoundExpr')) 155 | console.log 'rendering CompBoolExpr...' 156 | console.log @model.toString() 157 | 158 | #@leftView.render() 159 | #@rightView.render() 160 | 161 | #explicitly trigger change to force rendering 162 | @model.get('LeftTableField').trigger('change') 163 | @model.get('RightOperand').trigger('change') 164 | #@renderOperator() 165 | else 166 | $(@el).html(@template(@model.toJSON())) 167 | SQLPaneView.render() 168 | @ 169 | 170 | renderOperator: -> 171 | #renders everything but left and right operand 172 | #$(@el).html(@template(@model.toJSON())) 173 | #@.$('.operator').html(@template(@model.toJSON())) 174 | #@.$('.operator').html(@template(@model.toJSON()) 175 | $(@el).children('.operator').html(@template(@model.toJSON())) 176 | @ 177 | 178 | convertView2CompExpr: -> 179 | #renders 180 | $(@el).html(@template(@model.toJSON())) 181 | #@.$('.operator') 182 | #$(@el).closest('.operator') 183 | $(@el).children('.operator') 184 | .before($(@leftView.el)) #detach from existing DOM parent 185 | .after(@rightView.el) 186 | @renderOperator() 187 | 188 | setCachedView: -> 189 | WhereExprMgr.cachedView= @ 190 | 191 | addBoolExpr:(boolExpr, boolExprView) -> 192 | @bool2compound(boolExpr, boolExprView) 193 | 194 | bool2compound:(boolExpr, boolExprView) -> 195 | ### 196 | cache existing el.html 197 | create new view with this.model 198 | set view.el.html = cache.html 199 | view.el.addClass 'left' 200 | @leftView = view 201 | @rightView = boolExprView 202 | @rightView.addClass 'right' 203 | 204 | ### 205 | @model.addBoolExpr(boolExpr) 206 | #look out if child nodes get deleted 207 | #@model.getLeftTableField.bind 'remove',@childExprRemoved,@ 208 | #@model.getRightOperand.bind 'remove',@childExprRemoved,@ 209 | 210 | #@model.getLeftTableField.bind 'change:deleteOK',@childExprRemoved,@ 211 | #@model.getRightOperand.bind 'change:deleteOK',@childExprRemoved,@ 212 | 213 | @leftView = new BoolExprView( 214 | model: @model.get('LeftTableField') 215 | #className: 'bool expr left' 216 | el: $(@el).clone().addClass('bool expr left') 217 | ) #.render() 218 | #model for the new view will be the new LeftTableField 219 | 220 | $(boolExprView.el).addClass 'bool expr right' 221 | @rightView = boolExprView.render() 222 | @convertView2CompExpr() 223 | #@render() 224 | 225 | changeOperator: (evt)-> 226 | return 0 if $(@el).children('.operator').get(0) != evt.srcElement 227 | #@model.setOperator @.$('.operator').val() 228 | @model.setOperator $(evt.srcElement).val() 229 | 230 | clearLeftTableField: (evt)-> 231 | #return 0 if $(@el).children('.delete').get(0) != evt.srcElement 232 | #@model.set LeftTableField : null 233 | #BoolExprs.remove @model.getLeftTableField() #remove old expr 234 | #@model.getLeftTableField().destroy() 235 | deadExpr = @model.getLeftTableField() 236 | return 0 if deadExpr instanceof TableField 237 | liveExpr = @model.getRightOperand().toJSON() 238 | @model.set { 239 | LeftTableField : liveExpr.LeftTableField 240 | Operator: liveExpr.Operator 241 | RightOperand: liveExpr.RightOperand 242 | #isCompoundExpr: liveExpr.isCompoundExpr 243 | isCompoundExpr:false 244 | } 245 | #,{silent:true} 246 | #@initialize() 247 | #@model.set {isCompoundExpr : false},{silent:true} 248 | #@model.trigger('change') 249 | 250 | 251 | clearRightTableField: (evt)-> 252 | deadExpr = @model.getRightOperand() 253 | return 0 if deadExpr instanceof TableField 254 | liveExpr = @model.getLeftTableField().toJSON() 255 | @model.set { 256 | LeftTableField : liveExpr.LeftTableField 257 | Operator: liveExpr.Operator 258 | RightOperand: liveExpr.RightOperand 259 | #isCompoundExpr: liveExpr.isCompoundExpr 260 | isCompoundExpr:false 261 | } 262 | 263 | clearIfLast: -> 264 | if WhereExprMgr.el.find('.expr').size() == 1 265 | @resetWhereExpr() 266 | 267 | resetWhereExpr: -> 268 | #BoolExprs.reset() 269 | BoolExprs.each (m)-> 270 | m.destroy() 271 | WhereExprMgr.rootBoolExprView = null 272 | SQLPaneView.render() 273 | 274 | 275 | 276 | 277 | window.BoolExprList = Backbone.Collection.extend 278 | model: BoolExpr 279 | localStorage: new Store("BoolExprs") 280 | initialize: -> 281 | @bind("add",@createView,@) 282 | 283 | createView: (boolExpr)-> 284 | view = new BoolExprView( model: boolExpr ) 285 | WhereExprMgr.addAtRootLevel boolExpr, view 286 | SQLPaneView.render() 287 | 288 | window.BoolExprs = new BoolExprList() 289 | 290 | #manages Where exprs 291 | window.WhereExprMgr = { 292 | 293 | ### 294 | WhereExprMgr: -> 295 | BoolExprs.bind "add" 296 | ### 297 | addNewBoolExpr : (tableFld)-> 298 | console.log tableFld 299 | BoolExprs.create( LeftTableField: tableFld ) 300 | 301 | rootBoolExprView: null #stores the root node 302 | 303 | el: $('#pane-where') 304 | 305 | addAtRootLevel: (boolExpr,view) -> 306 | # sets rootBoolExprView OR Compounds with root 307 | if @rootBoolExprView is null 308 | @el.append(view.render().el) 309 | @rootBoolExprView=view 310 | else 311 | @rootBoolExprView.addBoolExpr(boolExpr,view) 312 | @rootBoolExprView.render() 313 | 314 | } 315 | 316 | ### 317 | WhereExprMgr.addNewBoolExpr TableFields.first() 318 | WhereExprMgr.addNewBoolExpr TableFields.last() 319 | ### 320 | #this must be moved in WhereExprMgr's constructor 321 | #BoolExprs.bind "add", WhereExprMgr.addOne, WhereExprMgr 322 | #BoolExprs.bind "change:LeftTableField", WhereExprMgr.addOne, WhereExprMgr 323 | 324 | #WhereExpr is a view for the root node of BoolExpr 325 | -------------------------------------------------------------------------------- /coffee/bool-expr.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | window.BoolExpr = Backbone.Model.extend({ 4 | defaults: function() { 5 | return { 6 | LeftTableField: null, 7 | RightOperand: '', 8 | Operator: Constants.EQUALS, 9 | isCompoundExpr: false, 10 | deleteOK: false 11 | }; 12 | }, 13 | set_isCompoundExpr: function() { 14 | return this.set({ 15 | isCompoundExpr: this.get('RightOperand') instanceof TableField 16 | }, { 17 | silent: true 18 | }); 19 | }, 20 | toString: function() { 21 | var op, rightOp; 22 | if (this.get('isCompoundExpr')) { 23 | return "(" + (this.get('LeftTableField').toString()) + " " + (this.get('Operator')) + " " + (this.get('RightOperand').toString()) + ")"; 24 | } else { 25 | op = this.get('Operator'); 26 | rightOp = this.get('RightOperand'); 27 | if (isNaN(rightOp) || op === Constants.LIKE) rightOp = "'" + rightOp + "'"; 28 | return "(" + (this.get('LeftTableField')) + " " + (this.get('Operator')) + " " + rightOp + ")"; 29 | } 30 | }, 31 | getLeftTableField: function() { 32 | return this.get('LeftTableField'); 33 | }, 34 | getRightOperand: function() { 35 | return this.get('RightOperand'); 36 | }, 37 | clear: function() { 38 | return this.set({ 39 | deleteOK: true 40 | }); 41 | }, 42 | isCompound: function() { 43 | return this.get('isCompoundExpr'); 44 | }, 45 | setOperator: function(newOp) { 46 | return this.set({ 47 | Operator: newOp 48 | }); 49 | }, 50 | addBoolExpr: function(boolExpr) { 51 | /* 52 | Turns self into CompBoolExpr form BoolExpr 53 | LeftTableField = clone 54 | RightOperand = boolExpr 55 | Operator = AND 56 | */ 57 | var newLeftTF, newM; 58 | console.log("Inside: addBoolExpr"); 59 | newM = this.toJSON(); 60 | newLeftTF = BoolExprs.create({ 61 | LeftTableField: newM.LeftTableField, 62 | RightOperand: newM.RightOperand, 63 | Operator: newM.Operator, 64 | isCompoundExpr: newM.isCompoundExpr 65 | }, { 66 | silent: true 67 | }); 68 | try { 69 | this.set({ 70 | LeftTableField: newLeftTF, 71 | RightOperand: boolExpr, 72 | Operator: Constants.AND 73 | }, { 74 | silent: true 75 | }); 76 | } catch (error) { 77 | console.log(error); 78 | } 79 | this.set({ 80 | isCompoundExpr: true 81 | }, { 82 | silent: true 83 | }); 84 | return this.trigger(); 85 | } 86 | }); 87 | window.BoolExprView = Backbone.View.extend({ 88 | template: _.template($('#bool-expr-template').html()), 89 | events: { 90 | "dragstart ": "setCachedView", 91 | "click .operator": "changeOperator", 92 | "blur .right-operand": "setRightOperand", 93 | "click .delete": "clearIfLast", 94 | "click .bool.expr.left > .delete": "clearLeftTableField", 95 | "click .bool.expr.right > .delete": "clearRightTableField" 96 | }, 97 | testHnd: function() { 98 | return console.log("handled"); 99 | }, 100 | setRightOperand: function(evt) { 101 | var val; 102 | if ($(this.el).children('.right-operand').get(0) === evt.srcElement) { 103 | val = this.$('.right-operand').val(); 104 | return this.model.set({ 105 | RightOperand: val 106 | }); 107 | } 108 | }, 109 | className: "expr", 110 | initialize: function() { 111 | this.model.bind('change', this.render, this); 112 | this.model.bind('change:isCompoundExpr', this.render, this); 113 | this.model.bind('destroy', this.remove, this); 114 | this.leftView = null; 115 | return this.rightView = null; 116 | }, 117 | render: function() { 118 | if (this.model.get('isCompoundExpr')) { 119 | console.log('rendering CompBoolExpr...'); 120 | console.log(this.model.toString()); 121 | this.model.get('LeftTableField').trigger('change'); 122 | this.model.get('RightOperand').trigger('change'); 123 | } else { 124 | $(this.el).html(this.template(this.model.toJSON())); 125 | SQLPaneView.render(); 126 | } 127 | return this; 128 | }, 129 | renderOperator: function() { 130 | $(this.el).children('.operator').html(this.template(this.model.toJSON())); 131 | return this; 132 | }, 133 | convertView2CompExpr: function() { 134 | $(this.el).html(this.template(this.model.toJSON())); 135 | $(this.el).children('.operator').before($(this.leftView.el)).after(this.rightView.el); 136 | return this.renderOperator(); 137 | }, 138 | setCachedView: function() { 139 | return WhereExprMgr.cachedView = this; 140 | }, 141 | addBoolExpr: function(boolExpr, boolExprView) { 142 | return this.bool2compound(boolExpr, boolExprView); 143 | }, 144 | bool2compound: function(boolExpr, boolExprView) { 145 | /* 146 | cache existing el.html 147 | create new view with this.model 148 | set view.el.html = cache.html 149 | view.el.addClass 'left' 150 | @leftView = view 151 | @rightView = boolExprView 152 | @rightView.addClass 'right' 153 | */ this.model.addBoolExpr(boolExpr); 154 | this.leftView = new BoolExprView({ 155 | model: this.model.get('LeftTableField'), 156 | el: $(this.el).clone().addClass('bool expr left') 157 | }); 158 | $(boolExprView.el).addClass('bool expr right'); 159 | this.rightView = boolExprView.render(); 160 | return this.convertView2CompExpr(); 161 | }, 162 | changeOperator: function(evt) { 163 | if ($(this.el).children('.operator').get(0) !== evt.srcElement) return 0; 164 | return this.model.setOperator($(evt.srcElement).val()); 165 | }, 166 | clearLeftTableField: function(evt) { 167 | var deadExpr, liveExpr; 168 | deadExpr = this.model.getLeftTableField(); 169 | if (deadExpr instanceof TableField) return 0; 170 | liveExpr = this.model.getRightOperand().toJSON(); 171 | return this.model.set({ 172 | LeftTableField: liveExpr.LeftTableField, 173 | Operator: liveExpr.Operator, 174 | RightOperand: liveExpr.RightOperand, 175 | isCompoundExpr: false 176 | }); 177 | }, 178 | clearRightTableField: function(evt) { 179 | var deadExpr, liveExpr; 180 | deadExpr = this.model.getRightOperand(); 181 | if (deadExpr instanceof TableField) return 0; 182 | liveExpr = this.model.getLeftTableField().toJSON(); 183 | return this.model.set({ 184 | LeftTableField: liveExpr.LeftTableField, 185 | Operator: liveExpr.Operator, 186 | RightOperand: liveExpr.RightOperand, 187 | isCompoundExpr: false 188 | }); 189 | }, 190 | clearIfLast: function() { 191 | if (WhereExprMgr.el.find('.expr').size() === 1) return this.resetWhereExpr(); 192 | }, 193 | resetWhereExpr: function() { 194 | BoolExprs.each(function(m) { 195 | return m.destroy(); 196 | }); 197 | WhereExprMgr.rootBoolExprView = null; 198 | return SQLPaneView.render(); 199 | } 200 | }); 201 | window.BoolExprList = Backbone.Collection.extend({ 202 | model: BoolExpr, 203 | localStorage: new Store("BoolExprs"), 204 | initialize: function() { 205 | return this.bind("add", this.createView, this); 206 | }, 207 | createView: function(boolExpr) { 208 | var view; 209 | view = new BoolExprView({ 210 | model: boolExpr 211 | }); 212 | WhereExprMgr.addAtRootLevel(boolExpr, view); 213 | return SQLPaneView.render(); 214 | } 215 | }); 216 | window.BoolExprs = new BoolExprList(); 217 | return window.WhereExprMgr = { 218 | /* 219 | WhereExprMgr: -> 220 | BoolExprs.bind "add" 221 | */ 222 | addNewBoolExpr: function(tableFld) { 223 | console.log(tableFld); 224 | return BoolExprs.create({ 225 | LeftTableField: tableFld 226 | }); 227 | }, 228 | rootBoolExprView: null, 229 | el: $('#pane-where'), 230 | addAtRootLevel: function(boolExpr, view) { 231 | if (this.rootBoolExprView === null) { 232 | this.el.append(view.render().el); 233 | return this.rootBoolExprView = view; 234 | } else { 235 | this.rootBoolExprView.addBoolExpr(boolExpr, view); 236 | return this.rootBoolExprView.render(); 237 | } 238 | } 239 | }; 240 | /* 241 | WhereExprMgr.addNewBoolExpr TableFields.first() 242 | WhereExprMgr.addNewBoolExpr TableFields.last() 243 | */ 244 | }); 245 | -------------------------------------------------------------------------------- /coffee/core.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Web-based Visual Query Designer is an open source software released under the MIT License 3 | ### 4 | 5 | $ -> 6 | window.Constants = { 7 | EQUALS : '=' 8 | LIKE : 'LIKE' 9 | LESS_THAN : '<' 10 | GREATER_THAN : '>' 11 | LESS_EQUAL : '<=' 12 | GREATER_EQUAL : '>=' 13 | NOT_EQUAL : '<>' 14 | AND : ' AND ' 15 | OR : ' OR ' 16 | } 17 | 18 | Constants.DIAG_CELL_HEIGHT = 18 19 | Constants.DIAG_UNIQUE_ID = "diag-table-" 20 | #Models 21 | window.TableField = Backbone.Model.extend 22 | defaults: -> 23 | ColumnName: "" 24 | Selected: false 25 | Alias: "" 26 | Sort: 'UNSORTED' 27 | SortOrder: 0 28 | IsPrimaryKey: "" 29 | SelectionOrder:0 30 | left: 0 31 | #top: 0 32 | setJoinLeft: -1 #value is irrelevant. used only for triggering change 33 | setJoinRight: -1 34 | 35 | chgJoinLeft: -> 36 | @set( setJoinLeft : @get('setJoinLeft')*(-1) ) 37 | 38 | chgJoinRight: -> 39 | @set( setJoinRight : @get('setJoinRight')*(-1) ) 40 | 41 | toggleSelected: -> 42 | if @get("Selected") 43 | @set 44 | SelectionOrder: 0 45 | Selected: false 46 | else 47 | @set 48 | SelectionOrder: TableFields.nextSelectionOrderNo( @.get('TableName') ) 49 | Selected: true 50 | 51 | setTop: (top) -> 52 | @set top:top 53 | 54 | setSortOrder: (newIdx) -> 55 | #accepts a value and sets only if Sort != UNSORTED 56 | @set {SortOrder: 57 | if @get('Sort')=='UNSORTED' then 0 else newIdx} 58 | 59 | changeTopLeft: (dx, dy) -> 60 | @set top: @get('top')+dy , silent:true 61 | @set left: @get('left')+dx , silent:true 62 | @trigger() 63 | 64 | toggleSort: -> 65 | if @.get('Sort') == 'UNSORTED' 66 | @.set Sort : 'ASC' 67 | else 68 | @.set Sort : 'UNSORTED' 69 | 70 | toString: ()-> 71 | @get('TableName') + "." + @get("ColumnName") 72 | 73 | 74 | # boolexpr code moved to bool-expr.coffee 75 | 76 | #Collections 77 | window.TableFieldList = Backbone.Collection.extend 78 | model: TableField 79 | localStorage: new Store("TableFields") 80 | 81 | nextSelectionOrderNo: (tableName)-> 82 | myTFs= @getModelsByTableName tableName 83 | lastSelOrder = _.max myTFs , (m) -> m.get('SelectionOrder') 84 | lastSelOrder = lastSelOrder.get 'SelectionOrder' 85 | return lastSelOrder+1 86 | 87 | getModelsByTableName: (tableName) -> 88 | @filter (m) -> m.get('TableName')== tableName 89 | 90 | getModelByTableCol: (tableName,colName) -> 91 | @find (m) -> m.get('TableName')== tableName and m.get('ColumnName')== colName 92 | 93 | getNextTF_Top: (tableName) -> 94 | myTFs = @getModelsByTableName tableName 95 | lastTFM = _.max myTFs , (m) -> m.get('top') 96 | if lastTFM? 97 | return lastTFM.get('top')+ Constants.DIAG_CELL_HEIGHT 98 | else 99 | return Constants.DIAG_CELL_HEIGHT 100 | 101 | window.TableFields = new TableFieldList() 102 | 103 | #View 104 | window.TableFieldView = Backbone.View.extend 105 | className: 'diag-cell' 106 | template: _.template($('#table-field-template').html()) 107 | events: 108 | "click input": "toggleSelected", 109 | "dragstart .cell-dragger": "setJoinLeftTable", 110 | "drop .cell-dragger": "setJoinRightTable" 111 | "click .orderby ": "toggleSort" 112 | #"click .add-condition ": "addToWhere" 113 | 114 | initialize: -> 115 | @model.bind('change', @render, this) 116 | @model.bind('change:setJoinLeft', @setJoinLeftTable, this) #used in MODIFY mode 117 | @model.bind('change:setJoinRight', @setJoinRightTable, this) #used in MODIFY mode 118 | @model.bind('destroy', @clear, this) #remove view if model is destroyed 119 | 120 | $(@el).draggable 121 | helper: "clone" 122 | cursor: "move" 123 | $(@el).droppable() 124 | #the model of respective Table seen at the top 125 | @options.GroupTable.bind 'change' , @reposition , this 126 | 127 | clear: -> 128 | jsPlumb.detachAllConnections @el 129 | @remove() 130 | 131 | render: -> 132 | $(@el) 133 | .css("top", @model.get("top")) 134 | .css("left", @model.get("left")) 135 | .css("position","absolute" ) 136 | $(@el).html(@template(@model.toJSON())) 137 | @ 138 | 139 | reposition : -> 140 | #console.log 'repositioning' 141 | dx = @options.GroupTable.get('chgX') 142 | dy = @options.GroupTable.get('chgY') 143 | @model.changeTopLeft dx, dy 144 | 145 | opacify: (what) -> 146 | 147 | $(@el).css opacity: if what then 0.5 else 1.0 148 | 149 | setJoinLeftTable : -> 150 | vqd.setJoinLeftField @el , @model.get('TableName'), @model.get('ColumnName') 151 | 152 | setJoinRightTable: -> 153 | vqd.completeJoin @el , @model.get('TableName'), @model.get('ColumnName') 154 | 155 | toggleSelected: -> 156 | @model.toggleSelected() 157 | 158 | toggleSort: -> 159 | @model.toggleSort() 160 | 161 | window.Table = Backbone.Model.extend 162 | defaults: -> 163 | TableName: '' 164 | left:0 165 | top:0 166 | chgX:0 167 | chgY:0 168 | NoOfFields:0 169 | 170 | incrFieldCounter: -> 171 | #increase fieldCounter 172 | @set NoOfFields: @get('NoOfFields')+1 173 | 174 | set_dx_dy:(dx,dy) -> 175 | @set {chgX : dx, chgY : dy} 176 | , silent:true 177 | @ 178 | 179 | changeTopLeft: (dx, dy) -> 180 | @set {top: @get('top')+dy } 181 | , silent:true 182 | @set {left: @get('left')+dx} 183 | , silent:true 184 | @ 185 | 186 | triggerChanges: -> 187 | @change() 188 | 189 | window.TableList = Backbone.Collection.extend 190 | model: Table 191 | localStorage: new Store("Tables") 192 | 193 | initialize: -> 194 | @bind('add',@addOne,this) 195 | 196 | addOne: (table) -> 197 | view = new TableView( model: table ) 198 | $('#design-pane').append(view.render().el) 199 | 200 | getModelByTableName: (tableName) -> 201 | @find (m) -> m.get('TableName')== tableName 202 | 203 | getLasttableName: -> 204 | @last(2)[0].get('TableName') #return last but one element since last element is the new element added 205 | 206 | window.Tables = new TableList() 207 | 208 | #instead of one big view for all TableFields 209 | #we have TableView(s) working for a group(single Table) 210 | #model : Table 211 | window.TableView = Backbone.View.extend 212 | className: 'diag-cell label' 213 | template: _.template($('#table-label-template').html()) 214 | events: 215 | "dragstop .label": "dragStopped" 216 | "click .close": "removeTable" 217 | 218 | initialize: -> 219 | TableFields.bind('add', @addOne, this) 220 | this.model.bind("change", this.render, this) 221 | 222 | $(@el).draggable 223 | helper: 'clone' 224 | cursor: 'move' 225 | #distance: 20 226 | 227 | $(@el) 228 | .css("position","absolute" ) 229 | 230 | addOne: (tf) -> 231 | tableName = tf.get('TableName') 232 | if tableName == @model.get('TableName') 233 | @model.incrFieldCounter() 234 | tf.setTop(TableFields.getNextTF_Top(tableName) ) 235 | view =new TableFieldView( model: tf , GroupTable: @model ) 236 | $('#design-pane').append view.render().el 237 | 238 | render: -> 239 | $(@el).css("top", @model.get('top')).css "left", @model.get('left') 240 | $(this.el).html(this.template(this.model.toJSON())) 241 | @ 242 | 243 | dragStopped: (event, ui) -> 244 | #console.log "drag Stopped" 245 | chgTop = ui.position.top - ui.originalPosition.top 246 | chgLeft = ui.position.left - ui.originalPosition.left 247 | @model.set_dx_dy chgLeft , chgTop 248 | @model.changeTopLeft chgLeft , chgTop 249 | @model.triggerChanges() 250 | 251 | jsPlumb.repaintEverything() 252 | 253 | 254 | removeTable: -> 255 | #console.log "removing table" 256 | tableName=@model.get('TableName') 257 | 258 | for mTF in TableFields.getModelsByTableName( tableName ) 259 | mTF.destroy() 260 | @model.destroy() 261 | @remove() 262 | Joins.removeJoinByTableName tableName 263 | removeSelection(tableName) 264 | 265 | 266 | window.AddTable= (tableName)-> 267 | #exit if table is already added 268 | if Tables.find( ((item) -> 269 | item.get('TableName') == tableName) 270 | , @) 271 | #console.log "Table #{tableName} already added" 272 | return 0 273 | $.ajax({ 274 | url: 'ajax/table_json.php', 275 | dataType: 'json', 276 | data: { table: tableName }, 277 | #async:false 278 | }).done( (data) -> 279 | SQLPaneView.listenEvents=false 280 | Tables.create TableName: tableName 281 | 282 | for d in data 283 | #console.time "creating TF" 284 | TableFields.create d 285 | #console.timeEnd "creating TF" 286 | SQLPaneView.listenEvents=true 287 | 288 | TableFields.trigger() 289 | SQLPaneView.render() 290 | ) 291 | 292 | #SQLPaneView.render() 293 | 294 | window.Join = Backbone.Model.extend 295 | defaults: -> 296 | Type: 'CROSS_JOIN' 297 | SelectionOrder: Joins.nextSelectionOrderNo() 298 | Fields: [] 299 | LeftField:'' 300 | RightField:'' 301 | ### 302 | LeftTable 303 | RightTable 304 | ### 305 | setSelectionOrder: (newIdx) -> 306 | @set {SelectionOrder: 307 | newIdx} 308 | 309 | joinOn : (tblLeft,colLeft,tblRight,colRight,joinType="INNER_JOIN") -> 310 | @set {Type: joinType} , silent:true 311 | 312 | if @get('LeftTable')== tblLeft 313 | @set LeftField : colLeft 314 | @set RightField : colRight 315 | else 316 | @set LeftField : colRight 317 | @set RightField : colLeft 318 | 319 | 320 | window.JoinView = Backbone.View.extend( 321 | initialize: -> 322 | @model.bind "change", @render, this 323 | 324 | render: -> 325 | jsPlumb.repaintEverything() 326 | ) 327 | 328 | window.JoinList = Backbone.Collection.extend 329 | model: Join 330 | localStorage: new Store("Joins") 331 | 332 | initialize: -> 333 | #Tables.bind "remove",@removeJoinByTableName,@ 334 | 335 | nextSelectionOrderNo: -> 336 | return 1 if @models.length is 0 337 | lastSelOrder = _.max @models , (m) -> m.get('SelectionOrder') 338 | lastSelOrder = lastSelOrder.get 'SelectionOrder' 339 | return lastSelOrder+1 340 | 341 | comparator: (m) -> 342 | m.get 'SelectionOrder' 343 | 344 | getJoinsByTableName: (tableName) -> 345 | @tableName = tableName 346 | @.filter (item) => 347 | m= item.toJSON() 348 | m.LeftTable == @tableName || m.RightTable==@tableName 349 | 350 | removeJoinByTableName: (tableName) -> 351 | #console.log 'removing join' 352 | for i in @getJoinsByTableName tableName 353 | i.destroy() 354 | 355 | window.Joins = new JoinList() 356 | 357 | ### 358 | misc. functions 359 | ### 360 | 361 | window.fetchTableResults = (tableName,qid)-> 362 | #fetches resultset of a table in JSON 363 | window.resultset = null 364 | $.ajax({ 365 | url: 'ajax/table_results.php', 366 | dataType: 'json', 367 | #data: { table: tableName , where: where }, 368 | data: { table: tableName , qid: qid }, 369 | #context: @ 370 | async:false 371 | }).done( (data) -> 372 | window.resultset = data 373 | ) 374 | window.resultset 375 | 376 | window.vqd = { 377 | 378 | getStringForConst: (constant) -> 379 | switch constant 380 | when 'INNER_JOIN' then return ' INNER JOIN ' 381 | setJoinLeftField : (el, tableName, columnName) -> 382 | @leftJoinEl = el 383 | @leftJoinColumn = { TableName: tableName , ColumnName: columnName } 384 | 385 | completeJoin : (el, tableName, columnName) -> 386 | my = @ #this alias 387 | # 388 | return 0 if @leftJoinColumn.TableName == tableName 389 | newJoinHost = Joins.find (m) -> 390 | (m.get('LeftTable') == vqd.leftJoinColumn.TableName and m.get('RightTable') == tableName) or 391 | (m.get('RightTable') == vqd.leftJoinColumn.TableName and m.get('LeftTable') == tableName) 392 | if (newJoinHost?) 393 | newJoinHost.joinOn(@leftJoinColumn.TableName, @leftJoinColumn.ColumnName,tableName, columnName) 394 | else 395 | Joins.create { 396 | LeftTable : @leftJoinColumn.TableName 397 | LeftField : @leftJoinColumn.ColumnName 398 | RightTable : tableName 399 | RightField : columnName 400 | Type : 'INNER_JOIN' 401 | } 402 | ConnectTableFields @leftJoinEl, el 403 | } 404 | 405 | ### 406 | Connect 407 | ### 408 | 409 | jsPlumb.connectorClass = "diag-join" 410 | window.ConnectTableFields = (elemStart, elemEnd) -> 411 | connectorPaintStyle = 412 | lineWidth: 5 413 | #strokeStyle: "#CC8A1A" 414 | strokeStyle: "#7794CE" 415 | 416 | common = 417 | anchor: [ "RightMiddle", "LeftMiddle" ] 418 | isSource: true 419 | endpoint: "Blank" 420 | isTarget: true 421 | 422 | e0 = jsPlumb.addEndpoint(elemStart, common, 423 | connectorStyle: connectorPaintStyle 424 | paintStyle: 425 | fillStyle: "#225588" 426 | radius: 7 427 | 428 | connector: [ "Flowchart" ] 429 | ) 430 | e1 = jsPlumb.addEndpoint(elemEnd, common, 431 | connectorStyle: connectorPaintStyle 432 | paintStyle: 433 | fillStyle: "#225588" 434 | radius: 7 435 | ) 436 | jsPlumb.connect 437 | source: e0 438 | target: e1 439 | overlays: [ [ "Label", 440 | label: "-" 441 | location: 0.5 442 | ] ] 443 | -------------------------------------------------------------------------------- /coffee/core.js: -------------------------------------------------------------------------------- 1 | /* 2 | Web-based Visual Query Designer is an open source software released under the MIT License 3 | */ 4 | $(function() { 5 | window.Constants = { 6 | EQUALS: '=', 7 | LIKE: 'LIKE', 8 | LESS_THAN: '<', 9 | GREATER_THAN: '>', 10 | LESS_EQUAL: '<=', 11 | GREATER_EQUAL: '>=', 12 | NOT_EQUAL: '<>', 13 | AND: ' AND ', 14 | OR: ' OR ' 15 | }; 16 | Constants.DIAG_CELL_HEIGHT = 18; 17 | Constants.DIAG_UNIQUE_ID = "diag-table-"; 18 | window.TableField = Backbone.Model.extend({ 19 | defaults: function() { 20 | return { 21 | ColumnName: "", 22 | Selected: false, 23 | Alias: "", 24 | Sort: 'UNSORTED', 25 | SortOrder: 0, 26 | IsPrimaryKey: "", 27 | SelectionOrder: 0, 28 | left: 0, 29 | setJoinLeft: -1, 30 | setJoinRight: -1 31 | }; 32 | }, 33 | chgJoinLeft: function() { 34 | return this.set({ 35 | setJoinLeft: this.get('setJoinLeft') * (-1) 36 | }); 37 | }, 38 | chgJoinRight: function() { 39 | return this.set({ 40 | setJoinRight: this.get('setJoinRight') * (-1) 41 | }); 42 | }, 43 | toggleSelected: function() { 44 | if (this.get("Selected")) { 45 | return this.set({ 46 | SelectionOrder: 0, 47 | Selected: false 48 | }); 49 | } else { 50 | return this.set({ 51 | SelectionOrder: TableFields.nextSelectionOrderNo(this.get('TableName')), 52 | Selected: true 53 | }); 54 | } 55 | }, 56 | setTop: function(top) { 57 | return this.set({ 58 | top: top 59 | }); 60 | }, 61 | setSortOrder: function(newIdx) { 62 | return this.set({ 63 | SortOrder: this.get('Sort') === 'UNSORTED' ? 0 : newIdx 64 | }); 65 | }, 66 | changeTopLeft: function(dx, dy) { 67 | this.set({ 68 | top: this.get('top') + dy, 69 | silent: true 70 | }); 71 | this.set({ 72 | left: this.get('left') + dx, 73 | silent: true 74 | }); 75 | return this.trigger(); 76 | }, 77 | toggleSort: function() { 78 | if (this.get('Sort') === 'UNSORTED') { 79 | return this.set({ 80 | Sort: 'ASC' 81 | }); 82 | } else { 83 | return this.set({ 84 | Sort: 'UNSORTED' 85 | }); 86 | } 87 | }, 88 | toString: function() { 89 | return this.get('TableName') + "." + this.get("ColumnName"); 90 | } 91 | }); 92 | window.TableFieldList = Backbone.Collection.extend({ 93 | model: TableField, 94 | localStorage: new Store("TableFields"), 95 | nextSelectionOrderNo: function(tableName) { 96 | var lastSelOrder, myTFs; 97 | myTFs = this.getModelsByTableName(tableName); 98 | lastSelOrder = _.max(myTFs, function(m) { 99 | return m.get('SelectionOrder'); 100 | }); 101 | lastSelOrder = lastSelOrder.get('SelectionOrder'); 102 | return lastSelOrder + 1; 103 | }, 104 | getModelsByTableName: function(tableName) { 105 | return this.filter(function(m) { 106 | return m.get('TableName') === tableName; 107 | }); 108 | }, 109 | getModelByTableCol: function(tableName, colName) { 110 | return this.find(function(m) { 111 | return m.get('TableName') === tableName && m.get('ColumnName') === colName; 112 | }); 113 | }, 114 | getNextTF_Top: function(tableName) { 115 | var lastTFM, myTFs; 116 | myTFs = this.getModelsByTableName(tableName); 117 | lastTFM = _.max(myTFs, function(m) { 118 | return m.get('top'); 119 | }); 120 | if (lastTFM != null) { 121 | return lastTFM.get('top') + Constants.DIAG_CELL_HEIGHT; 122 | } else { 123 | return Constants.DIAG_CELL_HEIGHT; 124 | } 125 | } 126 | }); 127 | window.TableFields = new TableFieldList(); 128 | window.TableFieldView = Backbone.View.extend({ 129 | className: 'diag-cell', 130 | template: _.template($('#table-field-template').html()), 131 | events: { 132 | "click input": "toggleSelected", 133 | "dragstart .cell-dragger": "setJoinLeftTable", 134 | "drop .cell-dragger": "setJoinRightTable", 135 | "click .orderby ": "toggleSort" 136 | }, 137 | initialize: function() { 138 | this.model.bind('change', this.render, this); 139 | this.model.bind('change:setJoinLeft', this.setJoinLeftTable, this); 140 | this.model.bind('change:setJoinRight', this.setJoinRightTable, this); 141 | this.model.bind('destroy', this.clear, this); 142 | $(this.el).draggable({ 143 | helper: "clone", 144 | cursor: "move" 145 | }); 146 | $(this.el).droppable(); 147 | return this.options.GroupTable.bind('change', this.reposition, this); 148 | }, 149 | clear: function() { 150 | jsPlumb.detachAllConnections(this.el); 151 | return this.remove(); 152 | }, 153 | render: function() { 154 | $(this.el).css("top", this.model.get("top")).css("left", this.model.get("left")).css("position", "absolute"); 155 | $(this.el).html(this.template(this.model.toJSON())); 156 | return this; 157 | }, 158 | reposition: function() { 159 | var dx, dy; 160 | dx = this.options.GroupTable.get('chgX'); 161 | dy = this.options.GroupTable.get('chgY'); 162 | return this.model.changeTopLeft(dx, dy); 163 | }, 164 | opacify: function(what) { 165 | return $(this.el).css({ 166 | opacity: what ? 0.5 : 1.0 167 | }); 168 | }, 169 | setJoinLeftTable: function() { 170 | return vqd.setJoinLeftField(this.el, this.model.get('TableName'), this.model.get('ColumnName')); 171 | }, 172 | setJoinRightTable: function() { 173 | return vqd.completeJoin(this.el, this.model.get('TableName'), this.model.get('ColumnName')); 174 | }, 175 | toggleSelected: function() { 176 | return this.model.toggleSelected(); 177 | }, 178 | toggleSort: function() { 179 | return this.model.toggleSort(); 180 | } 181 | }); 182 | window.Table = Backbone.Model.extend({ 183 | defaults: function() { 184 | return { 185 | TableName: '', 186 | left: 0, 187 | top: 0, 188 | chgX: 0, 189 | chgY: 0, 190 | NoOfFields: 0 191 | }; 192 | }, 193 | incrFieldCounter: function() { 194 | return this.set({ 195 | NoOfFields: this.get('NoOfFields') + 1 196 | }); 197 | }, 198 | set_dx_dy: function(dx, dy) { 199 | this.set({ 200 | chgX: dx, 201 | chgY: dy 202 | }, { 203 | silent: true 204 | }); 205 | return this; 206 | }, 207 | changeTopLeft: function(dx, dy) { 208 | this.set({ 209 | top: this.get('top') + dy 210 | }, { 211 | silent: true 212 | }); 213 | this.set({ 214 | left: this.get('left') + dx 215 | }, { 216 | silent: true 217 | }); 218 | return this; 219 | }, 220 | triggerChanges: function() { 221 | return this.change(); 222 | } 223 | }); 224 | window.TableList = Backbone.Collection.extend({ 225 | model: Table, 226 | localStorage: new Store("Tables"), 227 | initialize: function() { 228 | return this.bind('add', this.addOne, this); 229 | }, 230 | addOne: function(table) { 231 | var view; 232 | view = new TableView({ 233 | model: table 234 | }); 235 | return $('#design-pane').append(view.render().el); 236 | }, 237 | getModelByTableName: function(tableName) { 238 | return this.find(function(m) { 239 | return m.get('TableName') === tableName; 240 | }); 241 | }, 242 | getLasttableName: function() { 243 | return this.last(2)[0].get('TableName'); 244 | } 245 | }); 246 | window.Tables = new TableList(); 247 | window.TableView = Backbone.View.extend({ 248 | className: 'diag-cell label', 249 | template: _.template($('#table-label-template').html()), 250 | events: { 251 | "dragstop .label": "dragStopped", 252 | "click .close": "removeTable" 253 | }, 254 | initialize: function() { 255 | TableFields.bind('add', this.addOne, this); 256 | this.model.bind("change", this.render, this); 257 | $(this.el).draggable({ 258 | helper: 'clone', 259 | cursor: 'move' 260 | }); 261 | return $(this.el).css("position", "absolute"); 262 | }, 263 | addOne: function(tf) { 264 | var tableName, view; 265 | tableName = tf.get('TableName'); 266 | if (tableName === this.model.get('TableName')) { 267 | this.model.incrFieldCounter(); 268 | tf.setTop(TableFields.getNextTF_Top(tableName)); 269 | view = new TableFieldView({ 270 | model: tf, 271 | GroupTable: this.model 272 | }); 273 | return $('#design-pane').append(view.render().el); 274 | } 275 | }, 276 | render: function() { 277 | $(this.el).css("top", this.model.get('top')).css("left", this.model.get('left')); 278 | $(this.el).html(this.template(this.model.toJSON())); 279 | return this; 280 | }, 281 | dragStopped: function(event, ui) { 282 | var chgLeft, chgTop; 283 | chgTop = ui.position.top - ui.originalPosition.top; 284 | chgLeft = ui.position.left - ui.originalPosition.left; 285 | this.model.set_dx_dy(chgLeft, chgTop); 286 | this.model.changeTopLeft(chgLeft, chgTop); 287 | this.model.triggerChanges(); 288 | return jsPlumb.repaintEverything(); 289 | }, 290 | removeTable: function() { 291 | var mTF, tableName, _i, _len, _ref; 292 | tableName = this.model.get('TableName'); 293 | _ref = TableFields.getModelsByTableName(tableName); 294 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 295 | mTF = _ref[_i]; 296 | mTF.destroy(); 297 | } 298 | this.model.destroy(); 299 | this.remove(); 300 | Joins.removeJoinByTableName(tableName); 301 | return removeSelection(tableName); 302 | } 303 | }); 304 | return window.AddTable = function(tableName) { 305 | if (Tables.find((function(item) { 306 | return item.get('TableName') === tableName; 307 | }), this)) { 308 | return 0; 309 | } 310 | return $.ajax({ 311 | url: 'ajax/table_json.php', 312 | dataType: 'json', 313 | data: { 314 | table: tableName 315 | } 316 | }).done(function(data) { 317 | var d, _i, _len; 318 | SQLPaneView.listenEvents = false; 319 | Tables.create({ 320 | TableName: tableName 321 | }); 322 | for (_i = 0, _len = data.length; _i < _len; _i++) { 323 | d = data[_i]; 324 | TableFields.create(d); 325 | } 326 | SQLPaneView.listenEvents = true; 327 | TableFields.trigger(); 328 | return SQLPaneView.render(); 329 | }); 330 | }; 331 | }); 332 | 333 | window.Join = Backbone.Model.extend({ 334 | defaults: function() { 335 | return { 336 | Type: 'CROSS_JOIN', 337 | SelectionOrder: Joins.nextSelectionOrderNo(), 338 | Fields: [], 339 | LeftField: '', 340 | RightField: '' 341 | /* 342 | LeftTable 343 | RightTable 344 | */ 345 | }; 346 | }, 347 | setSelectionOrder: function(newIdx) { 348 | return this.set({ 349 | SelectionOrder: newIdx 350 | }); 351 | }, 352 | joinOn: function(tblLeft, colLeft, tblRight, colRight, joinType) { 353 | if (joinType == null) joinType = "INNER_JOIN"; 354 | this.set({ 355 | Type: joinType 356 | }, { 357 | silent: true 358 | }); 359 | if (this.get('LeftTable') === tblLeft) { 360 | this.set({ 361 | LeftField: colLeft 362 | }); 363 | return this.set({ 364 | RightField: colRight 365 | }); 366 | } else { 367 | this.set({ 368 | LeftField: colRight 369 | }); 370 | return this.set({ 371 | RightField: colLeft 372 | }); 373 | } 374 | } 375 | }); 376 | 377 | window.JoinView = Backbone.View.extend({ 378 | initialize: function() { 379 | return this.model.bind("change", this.render, this); 380 | }, 381 | render: function() { 382 | return jsPlumb.repaintEverything(); 383 | } 384 | }); 385 | 386 | window.JoinList = Backbone.Collection.extend({ 387 | model: Join, 388 | localStorage: new Store("Joins"), 389 | initialize: function() {}, 390 | nextSelectionOrderNo: function() { 391 | var lastSelOrder; 392 | if (this.models.length === 0) return 1; 393 | lastSelOrder = _.max(this.models, function(m) { 394 | return m.get('SelectionOrder'); 395 | }); 396 | lastSelOrder = lastSelOrder.get('SelectionOrder'); 397 | return lastSelOrder + 1; 398 | }, 399 | comparator: function(m) { 400 | return m.get('SelectionOrder'); 401 | }, 402 | getJoinsByTableName: function(tableName) { 403 | var _this = this; 404 | this.tableName = tableName; 405 | return this.filter(function(item) { 406 | var m; 407 | m = item.toJSON(); 408 | return m.LeftTable === _this.tableName || m.RightTable === _this.tableName; 409 | }); 410 | }, 411 | removeJoinByTableName: function(tableName) { 412 | var i, _i, _len, _ref, _results; 413 | _ref = this.getJoinsByTableName(tableName); 414 | _results = []; 415 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 416 | i = _ref[_i]; 417 | _results.push(i.destroy()); 418 | } 419 | return _results; 420 | } 421 | }); 422 | 423 | window.Joins = new JoinList(); 424 | 425 | /* 426 | misc. functions 427 | */ 428 | 429 | window.fetchTableResults = function(tableName, qid) { 430 | window.resultset = null; 431 | $.ajax({ 432 | url: 'ajax/table_results.php', 433 | dataType: 'json', 434 | data: { 435 | table: tableName, 436 | qid: qid 437 | }, 438 | async: false 439 | }).done(function(data) { 440 | return window.resultset = data; 441 | }); 442 | return window.resultset; 443 | }; 444 | 445 | window.vqd = { 446 | getStringForConst: function(constant) { 447 | switch (constant) { 448 | case 'INNER_JOIN': 449 | return ' INNER JOIN '; 450 | } 451 | }, 452 | setJoinLeftField: function(el, tableName, columnName) { 453 | this.leftJoinEl = el; 454 | return this.leftJoinColumn = { 455 | TableName: tableName, 456 | ColumnName: columnName 457 | }; 458 | }, 459 | completeJoin: function(el, tableName, columnName) { 460 | var my, newJoinHost; 461 | my = this; 462 | if (this.leftJoinColumn.TableName === tableName) return 0; 463 | newJoinHost = Joins.find(function(m) { 464 | return (m.get('LeftTable') === vqd.leftJoinColumn.TableName && m.get('RightTable') === tableName) || (m.get('RightTable') === vqd.leftJoinColumn.TableName && m.get('LeftTable') === tableName); 465 | }); 466 | if ((newJoinHost != null)) { 467 | newJoinHost.joinOn(this.leftJoinColumn.TableName, this.leftJoinColumn.ColumnName, tableName, columnName); 468 | } else { 469 | Joins.create({ 470 | LeftTable: this.leftJoinColumn.TableName, 471 | LeftField: this.leftJoinColumn.ColumnName, 472 | RightTable: tableName, 473 | RightField: columnName, 474 | Type: 'INNER_JOIN' 475 | }); 476 | } 477 | return ConnectTableFields(this.leftJoinEl, el); 478 | } 479 | }; 480 | 481 | /* 482 | Connect 483 | */ 484 | 485 | jsPlumb.connectorClass = "diag-join"; 486 | 487 | window.ConnectTableFields = function(elemStart, elemEnd) { 488 | var common, connectorPaintStyle, e0, e1; 489 | connectorPaintStyle = { 490 | lineWidth: 5, 491 | strokeStyle: "#7794CE" 492 | }; 493 | common = { 494 | anchor: ["RightMiddle", "LeftMiddle"], 495 | isSource: true, 496 | endpoint: "Blank", 497 | isTarget: true 498 | }; 499 | e0 = jsPlumb.addEndpoint(elemStart, common, { 500 | connectorStyle: connectorPaintStyle, 501 | paintStyle: { 502 | fillStyle: "#225588", 503 | radius: 7 504 | }, 505 | connector: ["Flowchart"] 506 | }); 507 | e1 = jsPlumb.addEndpoint(elemEnd, common, { 508 | connectorStyle: connectorPaintStyle, 509 | paintStyle: { 510 | fillStyle: "#225588", 511 | radius: 7 512 | } 513 | }); 514 | return jsPlumb.connect({ 515 | source: e0, 516 | target: e1, 517 | overlays: [ 518 | [ 519 | "Label", { 520 | label: "-", 521 | location: 0.5 522 | } 523 | ] 524 | ] 525 | }); 526 | }; 527 | -------------------------------------------------------------------------------- /coffee/core.panes.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | #panes 3 | 4 | ### 5 | SelectItemView 6 | ### 7 | window.SelectItemView = Backbone.View.extend { 8 | 9 | template: _.template($('#paneLi-select-template').html()) 10 | 11 | events: 12 | "blur input" : "setAlias" 13 | "click .delete" : "deselectField" 14 | 15 | initialize: -> 16 | @model.bind('change', @render, this) 17 | @model.bind('change:Selected', @moveToLastInPane, this) 18 | @model.bind('destroy', @remove, this) 19 | SelectPaneView.bind 'evtRearranged' , @setSelfSelectionOrder , this 20 | 21 | 22 | render: -> 23 | $(@el).html(@template(@model.toJSON())) 24 | return this 25 | 26 | deselectField : -> 27 | @model.toggleSelected() 28 | 29 | setSelfSelectionOrder: -> 30 | console.log 'setSelfSelectionOrder' 31 | @model.set {SelectionOrder: $(@el).index()} 32 | 33 | moveToLastInPane: -> 34 | if(@model.get('Selected')== false) 35 | # detach and store in el(?) so that SelectionOrder starts from 1 36 | return 37 | par = $(@el).parent() 38 | i = $(@el).detach() 39 | $(par).append(i) 40 | 41 | setAlias: -> 42 | console.log('setting alias') 43 | alias = $(@el).find('input').val() 44 | @model.set Alias: alias 45 | } 46 | 47 | 48 | #SelectPaneView 49 | window.SelectPaneView = 50 | new (Backbone.View.extend 51 | 52 | el: $('#pane-select') 53 | events: 54 | "sortstop div" : "SelectionOrder_DOM2model" 55 | 56 | initialize: -> 57 | TableFields.bind('add', @addOne, this) 58 | @el.sortable() 59 | 60 | addOne: (tf) -> 61 | view= new SelectItemView( model: tf ) 62 | @el.append view.render().el 63 | 64 | SelectionOrder_DOM2model: -> 65 | console.log 'SelectionOrder_DOM2model' 66 | @trigger 'evtRearranged' 67 | ) 68 | 69 | window.OrderbyItemView = Backbone.View.extend 70 | className: '' 71 | template: _.template($('#paneLI-orderby-template').html()) 72 | 73 | events: 74 | "change select": "changeSortOrder" 75 | "click .delete" : "removeSort" 76 | 77 | initialize: -> 78 | @model.bind('change', @render, this) 79 | @model.bind('destroy', @remove, this) 80 | OrderbyPaneView.bind 'evtRearranged' , @setSelfSortOrder , this 81 | 82 | render: -> 83 | $(@el).html(@template(@model.toJSON())) 84 | @ 85 | 86 | removeSort : () -> 87 | @model.toggleSort() #sets to UNSORTED 88 | 89 | changeSortOrder: -> 90 | console.log this.$('select').val() 91 | console.log 'changeSortOrder' 92 | @model.set Sort : this.$('select').val() 93 | return 94 | 95 | setSelfSortOrder: -> 96 | console.log 'setSelfSortOrder' 97 | @model.setSortOrder $(@el).index() 98 | 99 | 100 | window.OrderbyPaneView = 101 | new (Backbone.View.extend 102 | el: $('#pane-order-by') 103 | events: 104 | "sortstop div" : "SelectionOrder_DOM2model" 105 | 106 | initialize: -> 107 | TableFields.bind('add', @addOne, this) 108 | @el.sortable() 109 | 110 | addOne: (tf) -> 111 | view= new OrderbyItemView( model: tf ) 112 | @el.append view.render().el 113 | 114 | SelectionOrder_DOM2model: -> 115 | console.log 'SelectionOrder_DOM2model' 116 | @trigger 'evtRearranged' 117 | 118 | ) 119 | 120 | ### 121 | Joins 122 | ### 123 | 124 | window.JoinItemView = Backbone.View.extend 125 | className: '' 126 | template: _.template($('#paneLI-join-template').html()) 127 | events: 128 | "change select": "changeJoinType" 129 | #"click .delete" : "removeItem" 130 | 131 | initialize: -> 132 | @model.bind('change', @render, this) 133 | @model.bind('change:Selected', @moveToLastInPane, this) 134 | @model.bind('destroy', @remove, this) 135 | JoinPaneView.bind 'evtRearranged' , @setSelfSelectionOrder , this 136 | 137 | render: -> 138 | $(@el).html(@template(@model.toJSON())) 139 | @ 140 | 141 | changeJoinType: -> 142 | console.log this.$('select').val() 143 | @model.set Type : this.$('select').val() 144 | 145 | return 146 | 147 | setSelfSelectionOrder: -> 148 | console.log 'setSelfSelectionOrder' 149 | @model.setSelectionOrder $(@el).index() 150 | Joins.sort() 151 | 152 | moveToLastInPane: -> 153 | if(@model.get('Selected')== false) 154 | # detach and store in el(?) so that SelectionOrder starts from 1 155 | return 156 | par = $(@el).parent() 157 | i = $(@el).detach() 158 | $(par).append(i) 159 | 160 | 161 | window.JoinPaneView = 162 | new (Backbone.View.extend 163 | el: $('#pane-join') 164 | events: 165 | "sortstop div" : "SelectionOrder_DOM2model" 166 | 167 | initialize: -> 168 | Joins.bind('add', @addOne, this) 169 | #@el.sortable handle : '.rearrange' 170 | #@el.sortable() 171 | 172 | addOne: (jf) -> 173 | view= new JoinItemView( model: jf ) 174 | @el.append view.render().el 175 | 176 | SelectionOrder_DOM2model: -> 177 | console.log 'SelectionOrder_DOM2model:Join' 178 | @trigger 'evtRearranged' 179 | ) 180 | 181 | # 182 | # SQL generation 183 | # 184 | window.SQLPaneView = 185 | new ( Backbone.View.extend 186 | ### 187 | template: _.template($('#template').html()) 188 | events: 189 | ### 190 | 191 | el: $('.output > pre') 192 | initialize: -> 193 | TableFields.bind('change', @render, this) 194 | TableFields.bind('remove', @render, this) 195 | Joins.bind('add', @render, this) 196 | Joins.bind('change', @render, this) 197 | Joins.bind('reset', @render, this) 198 | Joins.bind('remove', @render, this) 199 | @listenEvents = true 200 | 201 | render: -> 202 | try 203 | #$(@el).html(@getSQL()) 204 | $(@el).html(@getSQL()) if @listenEvents 205 | catch error 206 | #console.log error #soln. : trigger change event after all models are fetched 207 | finally 208 | @ 209 | 210 | getFullFieldName : (Alias=true)-> 211 | #ret = "`" + @TableName + "`.`" + @ColumnName + "`" 212 | ret = "" + @TableName + "." + @ColumnName + "" 213 | if Alias 214 | ret += " AS " + @Alias unless @Alias is "" 215 | ret 216 | 217 | getOrderbyPart: -> 218 | switch @Sort 219 | when 'ASC' then str= '' 220 | when 'DESC' then str= 'DESC' #the text assigned to str is of SQL syntax 221 | else str='' #UNSORTED 222 | str 223 | 224 | getJoinPart: -> 225 | joinCnt = Joins.length 226 | #return just the table name if only single table is added 227 | if joinCnt == 0 228 | return TableFields.first().get('TableName') 229 | 230 | ret='' 231 | ret += Array( joinCnt + 1 ).join '(' #opening brackets for all joins 232 | ret += @toJoinString.call(Joins.first().toJSON() ,true) 233 | for jfm in Joins.toJSON()[1..joinCnt ] 234 | ret += @toJoinString.call(jfm) 235 | return ret 236 | 237 | toJoinString : (isFirst=false) -> 238 | getJoinName = (constName) -> 239 | switch constName 240 | when 'INNER_JOIN' then return ' INNER JOIN ' 241 | when 'CROSS_JOIN' then return ' CROSS JOIN ' 242 | when 'LEFT_OUTER' then return ' LEFT OUTER JOIN ' 243 | when 'RIGHT_OUTER' then return ' RIGHT OUTER JOIN ' 244 | 245 | #ret = "#{ if isFirst then @LeftTable else '\r\n\t\t' } #{@Type} #{@RightTable} " 246 | ret = "#{ if isFirst then @LeftTable else '\r\n\t\t' } #{getJoinName( @Type) } #{@RightTable} " 247 | if @Type != 'CROSS_JOIN' 248 | #ret += " ON " + (" #{@LeftTable}.#{col[0]} = #{@RightTable}.#{col[1]}" for col in @Fields ).join(' AND ') 249 | ret += " ON " + (" #{@LeftTable}.#{@LeftField} = #{@RightTable}.#{@RightField}" ) 250 | return ret + ")" 251 | 252 | getWherePart: -> 253 | #"CustName LIKE 'Prof%'" 254 | #"1=1" 255 | 256 | $('#pane-where textarea').val() 257 | 258 | getSQL: -> 259 | #Select part 260 | selectedFields = _.sortBy ( m for m in TableFields.toJSON() when m.Selected ) , 261 | (i) -> i.SelectionOrder 262 | selectPart = ( @getFullFieldName.apply(f) for f in selectedFields ).join(", ") 263 | 264 | #join 265 | joinPart = @getJoinPart() 266 | 267 | #where 268 | wherePart = @getWherePart() 269 | #order by 270 | orderbyFields = _.sortBy ( m for m in TableFields.toJSON() when m.Sort != 'UNSORTED' ) , 271 | (i) -> i.SortOrder 272 | orderbyPart = ( @getFullFieldName.call(f,false) + ' ' + @getOrderbyPart.apply(f) for f in orderbyFields ).join(", ") 273 | 274 | ret = "SELECT \n\t#{selectPart} \n" 275 | ret +="FROM \n\t#{joinPart} \n" 276 | if wherePart != "" 277 | ret +="WHERE \n\t#{wherePart} \n" 278 | if orderbyPart != "" 279 | ret +="ORDER BY \n\t#{orderbyPart}" 280 | else 281 | ret += "\n\n" 282 | return ret 283 | 284 | ) 285 | 286 | _.extend(SelectPaneView, Backbone.Events) 287 | _.extend(OrderbyPaneView, Backbone.Events) 288 | _.extend(JoinPaneView, Backbone.Events) 289 | -------------------------------------------------------------------------------- /coffee/core.panes.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | /* 4 | SelectItemView 5 | */ window.SelectItemView = Backbone.View.extend({ 6 | template: _.template($('#paneLi-select-template').html()), 7 | events: { 8 | "blur input": "setAlias", 9 | "click .delete": "deselectField" 10 | }, 11 | initialize: function() { 12 | this.model.bind('change', this.render, this); 13 | this.model.bind('change:Selected', this.moveToLastInPane, this); 14 | this.model.bind('destroy', this.remove, this); 15 | return SelectPaneView.bind('evtRearranged', this.setSelfSelectionOrder, this); 16 | }, 17 | render: function() { 18 | $(this.el).html(this.template(this.model.toJSON())); 19 | return this; 20 | }, 21 | deselectField: function() { 22 | return this.model.toggleSelected(); 23 | }, 24 | setSelfSelectionOrder: function() { 25 | console.log('setSelfSelectionOrder'); 26 | return this.model.set({ 27 | SelectionOrder: $(this.el).index() 28 | }); 29 | }, 30 | moveToLastInPane: function() { 31 | var i, par; 32 | if (this.model.get('Selected') === false) return; 33 | par = $(this.el).parent(); 34 | i = $(this.el).detach(); 35 | return $(par).append(i); 36 | }, 37 | setAlias: function() { 38 | var alias; 39 | console.log('setting alias'); 40 | alias = $(this.el).find('input').val(); 41 | return this.model.set({ 42 | Alias: alias 43 | }); 44 | } 45 | }); 46 | window.SelectPaneView = new (Backbone.View.extend({ 47 | el: $('#pane-select'), 48 | events: { 49 | "sortstop div": "SelectionOrder_DOM2model" 50 | }, 51 | initialize: function() { 52 | TableFields.bind('add', this.addOne, this); 53 | return this.el.sortable(); 54 | }, 55 | addOne: function(tf) { 56 | var view; 57 | view = new SelectItemView({ 58 | model: tf 59 | }); 60 | return this.el.append(view.render().el); 61 | }, 62 | SelectionOrder_DOM2model: function() { 63 | console.log('SelectionOrder_DOM2model'); 64 | return this.trigger('evtRearranged'); 65 | } 66 | })); 67 | window.OrderbyItemView = Backbone.View.extend({ 68 | className: '', 69 | template: _.template($('#paneLI-orderby-template').html()), 70 | events: { 71 | "change select": "changeSortOrder", 72 | "click .delete": "removeSort" 73 | }, 74 | initialize: function() { 75 | this.model.bind('change', this.render, this); 76 | this.model.bind('destroy', this.remove, this); 77 | return OrderbyPaneView.bind('evtRearranged', this.setSelfSortOrder, this); 78 | }, 79 | render: function() { 80 | $(this.el).html(this.template(this.model.toJSON())); 81 | return this; 82 | }, 83 | removeSort: function() { 84 | return this.model.toggleSort(); 85 | }, 86 | changeSortOrder: function() { 87 | console.log(this.$('select').val()); 88 | console.log('changeSortOrder'); 89 | this.model.set({ 90 | Sort: this.$('select').val() 91 | }); 92 | }, 93 | setSelfSortOrder: function() { 94 | console.log('setSelfSortOrder'); 95 | return this.model.setSortOrder($(this.el).index()); 96 | } 97 | }); 98 | window.OrderbyPaneView = new (Backbone.View.extend({ 99 | el: $('#pane-order-by'), 100 | events: { 101 | "sortstop div": "SelectionOrder_DOM2model" 102 | }, 103 | initialize: function() { 104 | TableFields.bind('add', this.addOne, this); 105 | return this.el.sortable(); 106 | }, 107 | addOne: function(tf) { 108 | var view; 109 | view = new OrderbyItemView({ 110 | model: tf 111 | }); 112 | return this.el.append(view.render().el); 113 | }, 114 | SelectionOrder_DOM2model: function() { 115 | console.log('SelectionOrder_DOM2model'); 116 | return this.trigger('evtRearranged'); 117 | } 118 | })); 119 | /* 120 | Joins 121 | */ 122 | window.JoinItemView = Backbone.View.extend({ 123 | className: '', 124 | template: _.template($('#paneLI-join-template').html()), 125 | events: { 126 | "change select": "changeJoinType" 127 | }, 128 | initialize: function() { 129 | this.model.bind('change', this.render, this); 130 | this.model.bind('change:Selected', this.moveToLastInPane, this); 131 | this.model.bind('destroy', this.remove, this); 132 | return JoinPaneView.bind('evtRearranged', this.setSelfSelectionOrder, this); 133 | }, 134 | render: function() { 135 | $(this.el).html(this.template(this.model.toJSON())); 136 | return this; 137 | }, 138 | changeJoinType: function() { 139 | console.log(this.$('select').val()); 140 | this.model.set({ 141 | Type: this.$('select').val() 142 | }); 143 | }, 144 | setSelfSelectionOrder: function() { 145 | console.log('setSelfSelectionOrder'); 146 | this.model.setSelectionOrder($(this.el).index()); 147 | return Joins.sort(); 148 | }, 149 | moveToLastInPane: function() { 150 | var i, par; 151 | if (this.model.get('Selected') === false) return; 152 | par = $(this.el).parent(); 153 | i = $(this.el).detach(); 154 | return $(par).append(i); 155 | } 156 | }); 157 | window.JoinPaneView = new (Backbone.View.extend({ 158 | el: $('#pane-join'), 159 | events: { 160 | "sortstop div": "SelectionOrder_DOM2model" 161 | }, 162 | initialize: function() { 163 | return Joins.bind('add', this.addOne, this); 164 | }, 165 | addOne: function(jf) { 166 | var view; 167 | view = new JoinItemView({ 168 | model: jf 169 | }); 170 | return this.el.append(view.render().el); 171 | }, 172 | SelectionOrder_DOM2model: function() { 173 | console.log('SelectionOrder_DOM2model:Join'); 174 | return this.trigger('evtRearranged'); 175 | } 176 | })); 177 | window.SQLPaneView = new (Backbone.View.extend({ 178 | /* 179 | template: _.template($('#template').html()) 180 | events: 181 | */ 182 | el: $('.output > pre'), 183 | initialize: function() { 184 | TableFields.bind('change', this.render, this); 185 | TableFields.bind('remove', this.render, this); 186 | Joins.bind('add', this.render, this); 187 | Joins.bind('change', this.render, this); 188 | Joins.bind('reset', this.render, this); 189 | Joins.bind('remove', this.render, this); 190 | return this.listenEvents = true; 191 | }, 192 | render: function() { 193 | try { 194 | if (this.listenEvents) return $(this.el).html(this.getSQL()); 195 | } catch (error) { 196 | 197 | } finally { 198 | this; 199 | } 200 | }, 201 | getFullFieldName: function(Alias) { 202 | var ret; 203 | if (Alias == null) Alias = true; 204 | ret = "" + this.TableName + "." + this.ColumnName + ""; 205 | if (Alias) if (this.Alias !== "") ret += " AS " + this.Alias; 206 | return ret; 207 | }, 208 | getOrderbyPart: function() { 209 | var str; 210 | switch (this.Sort) { 211 | case 'ASC': 212 | str = ''; 213 | break; 214 | case 'DESC': 215 | str = 'DESC'; 216 | break; 217 | default: 218 | str = ''; 219 | } 220 | return str; 221 | }, 222 | getJoinPart: function() { 223 | var jfm, joinCnt, ret, _i, _len, _ref; 224 | joinCnt = Joins.length; 225 | if (joinCnt === 0) return TableFields.first().get('TableName'); 226 | ret = ''; 227 | ret += Array(joinCnt + 1).join('('); 228 | ret += this.toJoinString.call(Joins.first().toJSON(), true); 229 | _ref = Joins.toJSON().slice(1, joinCnt + 1 || 9e9); 230 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 231 | jfm = _ref[_i]; 232 | ret += this.toJoinString.call(jfm); 233 | } 234 | return ret; 235 | }, 236 | toJoinString: function(isFirst) { 237 | var getJoinName, ret; 238 | if (isFirst == null) isFirst = false; 239 | getJoinName = function(constName) { 240 | switch (constName) { 241 | case 'INNER_JOIN': 242 | return ' INNER JOIN '; 243 | case 'CROSS_JOIN': 244 | return ' CROSS JOIN '; 245 | case 'LEFT_OUTER': 246 | return ' LEFT OUTER JOIN '; 247 | case 'RIGHT_OUTER': 248 | return ' RIGHT OUTER JOIN '; 249 | } 250 | }; 251 | ret = "" + (isFirst ? this.LeftTable : '\r\n\t\t') + " " + (getJoinName(this.Type)) + " " + this.RightTable + " "; 252 | if (this.Type !== 'CROSS_JOIN') { 253 | ret += " ON " + (" " + this.LeftTable + "." + this.LeftField + " = " + this.RightTable + "." + this.RightField); 254 | } 255 | return ret + ")"; 256 | }, 257 | getWherePart: function() { 258 | return $('#pane-where textarea').val(); 259 | }, 260 | getSQL: function() { 261 | var f, joinPart, m, orderbyFields, orderbyPart, ret, selectPart, selectedFields, wherePart; 262 | selectedFields = _.sortBy((function() { 263 | var _i, _len, _ref, _results; 264 | _ref = TableFields.toJSON(); 265 | _results = []; 266 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 267 | m = _ref[_i]; 268 | if (m.Selected) _results.push(m); 269 | } 270 | return _results; 271 | })(), function(i) { 272 | return i.SelectionOrder; 273 | }); 274 | selectPart = ((function() { 275 | var _i, _len, _results; 276 | _results = []; 277 | for (_i = 0, _len = selectedFields.length; _i < _len; _i++) { 278 | f = selectedFields[_i]; 279 | _results.push(this.getFullFieldName.apply(f)); 280 | } 281 | return _results; 282 | }).call(this)).join(", "); 283 | joinPart = this.getJoinPart(); 284 | wherePart = this.getWherePart(); 285 | orderbyFields = _.sortBy((function() { 286 | var _i, _len, _ref, _results; 287 | _ref = TableFields.toJSON(); 288 | _results = []; 289 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 290 | m = _ref[_i]; 291 | if (m.Sort !== 'UNSORTED') _results.push(m); 292 | } 293 | return _results; 294 | })(), function(i) { 295 | return i.SortOrder; 296 | }); 297 | orderbyPart = ((function() { 298 | var _i, _len, _results; 299 | _results = []; 300 | for (_i = 0, _len = orderbyFields.length; _i < _len; _i++) { 301 | f = orderbyFields[_i]; 302 | _results.push(this.getFullFieldName.call(f, false) + ' ' + this.getOrderbyPart.apply(f)); 303 | } 304 | return _results; 305 | }).call(this)).join(", "); 306 | ret = "SELECT \n\t" + selectPart + " \n"; 307 | ret += "FROM \n\t" + joinPart + " \n"; 308 | if (wherePart !== "") ret += "WHERE \n\t" + wherePart + " \n"; 309 | if (orderbyPart !== "") { 310 | ret += "ORDER BY \n\t" + orderbyPart; 311 | } else { 312 | ret += "\n\n"; 313 | } 314 | return ret; 315 | } 316 | })); 317 | _.extend(SelectPaneView, Backbone.Events); 318 | _.extend(OrderbyPaneView, Backbone.Events); 319 | return _.extend(JoinPaneView, Backbone.Events); 320 | }); 321 | -------------------------------------------------------------------------------- /coffee/design.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | $('#design-results-btns .btn') 3 | .on "click", (event)-> 4 | if($(this).hasClass('selected')) 5 | #no-op 6 | return 7 | else 8 | #toggle mode 9 | $('#right-pane').toggleClass('design-mode results-mode') 10 | $('#design-results-btns .selected').removeClass('selected') 11 | $(this).addClass('selected') 12 | 13 | if $('#right-pane').hasClass('results-mode') 14 | sqlOut = QueryMaint.getSQL() 15 | fillResultData(sqlOut) 16 | 17 | fillResultData = (sql)-> 18 | $.ajax({ 19 | url:"query_results.php" 20 | data:{ sql: sql } 21 | cache:false 22 | 23 | }).done( (html) -> 24 | $('#results-pane').html(html) 25 | ) 26 | 27 | 28 | #set design-mode as default 29 | $('#design-results-btns .design-mode').trigger('click') 30 | 31 | $('#btn-save') 32 | .on "click", (event)-> 33 | #alert('save clicked') 34 | QueryMaint.save() 35 | 36 | $('#btn-edit-sql') 37 | .on "click", (event)-> 38 | # toggle output/input mode 39 | console.log 'editing sql...' 40 | curMode = if $('#sql-out').hasClass('output-mode') then 'output' else 'input' 41 | if curMode == 'output' 42 | #hide output 43 | #copy text to input textarea 44 | sql = $('#sql-text-op').text() 45 | $('#sql-text-ip').html(sql) 46 | 47 | #change btn label to Done editing 48 | $(this).html('Done editing') 49 | 50 | else #input 51 | #hide input 52 | newSQL = $('#sql-text-ip').val() 53 | SQLDataRestorer.init(0, newSQL) 54 | #change btn label to Edit SQL 55 | $(this).html('Edit SQL') 56 | 57 | $('#sql-out').toggleClass 'output-mode input-mode' 58 | 59 | $('select[name=schema]') 60 | .on 'blur', (evt)-> 61 | schema = $(evt.target).val() 62 | if $('select[name=schema]').val() == "" 63 | App.reset() 64 | 65 | window.LoadTableList = (schema)-> 66 | if $.cookie('schema') == schema then return #no loading required 67 | $.cookie 'schema', schema 68 | $.ajax({ 69 | url:"ajax/table_list.php" 70 | data:{ schema: schema } 71 | cache:false 72 | }).done( (html) -> 73 | $('#table-list').html(html) 74 | bindAddTableEvts() 75 | ) 76 | 77 | $('#pane-where textarea') 78 | .on 'keyup', _.debounce( 79 | (event) -> SQLPaneView.render(), 80 | 500) 81 | 82 | window.App = { 83 | reset : -> 84 | $('select[name=schema]').val("") 85 | if localStorage then localStorage.clear() 86 | @setAppVisibility() 87 | SQLPaneView.render() 88 | 89 | setAppVisibility : -> 90 | #sets visibility of all but basic (top) ctrls 91 | show = $('select[name=schema]').val() != "" 92 | if show 93 | $('.pane').fadeIn(500) 94 | else 95 | $('.pane').fadeOut(0) 96 | 97 | } 98 | -------------------------------------------------------------------------------- /coffee/design.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | var fillResultData; 4 | $('#design-results-btns .btn').on("click", function(event) { 5 | var sqlOut; 6 | if ($(this).hasClass('selected')) { 7 | return; 8 | } else { 9 | $('#right-pane').toggleClass('design-mode results-mode'); 10 | $('#design-results-btns .selected').removeClass('selected'); 11 | $(this).addClass('selected'); 12 | } 13 | if ($('#right-pane').hasClass('results-mode')) { 14 | sqlOut = QueryMaint.getSQL(); 15 | return fillResultData(sqlOut); 16 | } 17 | }); 18 | fillResultData = function(sql) { 19 | return $.ajax({ 20 | url: "query_results.php", 21 | data: { 22 | sql: sql 23 | }, 24 | cache: false 25 | }).done(function(html) { 26 | return $('#results-pane').html(html); 27 | }); 28 | }; 29 | $('#design-results-btns .design-mode').trigger('click'); 30 | $('#btn-save').on("click", function(event) { 31 | return QueryMaint.save(); 32 | }); 33 | $('#btn-edit-sql').on("click", function(event) { 34 | var curMode, newSQL, sql; 35 | console.log('editing sql...'); 36 | curMode = $('#sql-out').hasClass('output-mode') ? 'output' : 'input'; 37 | if (curMode === 'output') { 38 | sql = $('#sql-text-op').text(); 39 | $('#sql-text-ip').html(sql); 40 | $(this).html('Done editing'); 41 | } else { 42 | newSQL = $('#sql-text-ip').val(); 43 | SQLDataRestorer.init(0, newSQL); 44 | $(this).html('Edit SQL'); 45 | } 46 | return $('#sql-out').toggleClass('output-mode input-mode'); 47 | }); 48 | $('select[name=schema]').on('blur', function(evt) { 49 | var schema; 50 | schema = $(evt.target).val(); 51 | if ($('select[name=schema]').val() === "") return App.reset(); 52 | }); 53 | window.LoadTableList = function(schema) { 54 | if ($.cookie('schema') === schema) return; 55 | $.cookie('schema', schema); 56 | return $.ajax({ 57 | url: "ajax/table_list.php", 58 | data: { 59 | schema: schema 60 | }, 61 | cache: false 62 | }).done(function(html) { 63 | $('#table-list').html(html); 64 | return bindAddTableEvts(); 65 | }); 66 | }; 67 | $('#pane-where textarea').on('keyup', _.debounce(function(event) { 68 | return SQLPaneView.render(); 69 | }, 500)); 70 | return window.App = { 71 | reset: function() { 72 | $('select[name=schema]').val(""); 73 | if (localStorage) localStorage.clear(); 74 | this.setAppVisibility(); 75 | return SQLPaneView.render(); 76 | }, 77 | setAppVisibility: function() { 78 | var show; 79 | show = $('select[name=schema]').val() !== ""; 80 | if (show) { 81 | return $('.pane').fadeIn(500); 82 | } else { 83 | return $('.pane').fadeOut(0); 84 | } 85 | } 86 | }; 87 | }); 88 | -------------------------------------------------------------------------------- /coffee/save.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | var QueryMaintClass; 4 | QueryMaintClass = (function() { 5 | 6 | function QueryMaintClass() {} 7 | 8 | QueryMaintClass.prototype.init = function(qname, qid) { 9 | if (qname == null) qname = ""; 10 | if (qid == null) qid = 0; 11 | this.QueryName = qname; 12 | this.sql = ''; 13 | this.QID = qid; 14 | if (this.QID > 0) return SQLDataRestorer.init(this.QID); 15 | }; 16 | 17 | QueryMaintClass.prototype.setQueryName = function(qname) { 18 | this.qname = qname; 19 | }; 20 | 21 | QueryMaintClass.prototype.getSQL = function() { 22 | return $(SQLPaneView.el).text(); 23 | }; 24 | 25 | QueryMaintClass.prototype.getQueryNameFromUser = function() { 26 | return this.QueryName = prompt("Enter query name"); 27 | }; 28 | 29 | QueryMaintClass.prototype.save = function() { 30 | /* 31 | mQuery 32 | QueryName = this.QueryName 33 | SQLText = this.sql 34 | SchemaName 35 | 36 | dQuery 37 | TableName = TableField.TableName 38 | ColumnName = TableField.ColumnName 39 | OffSetX = left 40 | OffSetY = top 41 | */ 42 | var d, data_dQuery, data_mQuery, _i, _len, _ref; 43 | console.log('saving...'); 44 | while (this.QueryName === '' || !this.QueryName) { 45 | this.getQueryNameFromUser(); 46 | if (this.QueryName === '' || !this.QueryName) alert('invalid query name'); 47 | } 48 | this.sql = this.getSQL(); 49 | data_mQuery = { 50 | QID: this.QID, 51 | QueryName: this.QueryName, 52 | SQLText: this.sql 53 | }; 54 | data_dQuery = []; 55 | _ref = TableFields.toJSON(); 56 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 57 | d = _ref[_i]; 58 | data_dQuery.push({ 59 | TableName: d.TableName, 60 | ColumnName: d.ColumnName, 61 | OffSetX: d.left, 62 | OffSetY: d.top 63 | }); 64 | } 65 | console.log(data_mQuery); 66 | console.log(data_dQuery); 67 | return $.ajax({ 68 | type: "GET", 69 | url: "ajax/save_query.php", 70 | data: { 71 | mQuery: data_mQuery, 72 | dQuery: data_dQuery 73 | }, 74 | dataType: "json", 75 | context: QueryMaint 76 | }).done(function(data) { 77 | /* 78 | msg = $.trim msg 79 | if msg == 'true' 80 | window.save_status = true 81 | window.last_saved_at = new Date() 82 | alert "Saved successfully" 83 | */ console.log(data.query_m); 84 | console.log(data.query_d); 85 | if (this.QID === 0) { 86 | this.QID = data.QID; 87 | $('#QueryName').html(this.QueryName); 88 | } 89 | return alert("Saved successfully"); 90 | }); 91 | /* 92 | if window.save_status 93 | alert "Saved successfully" 94 | */ 95 | }; 96 | 97 | return QueryMaintClass; 98 | 99 | })(); 100 | return window.QueryMaint = new QueryMaintClass(); 101 | }); 102 | -------------------------------------------------------------------------------- /coffee/sql2diag.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | window.JoinDesc = { 4 | 'INNER': 'INNER_JOIN', 5 | 'LEFT': 'LEFT_OUTER', 6 | 'RIGHT': 'RIGHT_OUTER', 7 | 'CROSS': 'CROSS_JOIN' 8 | }; 9 | /* 10 | store SQLText 11 | 12 | fetch JSON data from dQuery 13 | for each table, AddTable 14 | using this data, create TableField and put in TableFields 15 | Models 16 | TableField 17 | Table 18 | Join 19 | BoolExpr 20 | 21 | Update 22 | TableField 23 | OffSet X,Y 24 | Selected 25 | SelectionOrder 26 | SortOrder 27 | 28 | 29 | Table 30 | NoOfFields 31 | TableName 32 | left 33 | top 34 | 35 | Create 36 | Join 37 | LeftField: "DCID" 38 | LeftTable: "mDC" 39 | RightField: "DCID" 40 | RightTable: "dDC" 41 | SelectionOrder: 1 42 | Type: "INNER_JOIN" 43 | 44 | 45 | 46 | Parse SQL , create Join 47 | 48 | 49 | 50 | parse SQLText 51 | parse SELECT part 52 | update TableFields 53 | parse FROM part 54 | create Join in Joins 55 | parse ORDER BY 56 | update TableFields 57 | parse WHERE part 58 | ? 59 | create boolexpr objects 60 | */ 61 | /* 62 | The role of these objects is very short lived 63 | Used only in modify mode, for restoring the Models and other stuff 64 | */ 65 | window.SQLParser = { 66 | parseSQL: function(sql) { 67 | if (!sql) sql = this.SQLText; 68 | this.SQLDataObject.reset(); 69 | this.SQLText = sql; 70 | this.parseSelectClause(); 71 | this.parseJoinClause(); 72 | this.parseOrderByClause(); 73 | return this.SQLDataObject; 74 | }, 75 | SQLDataObject: { 76 | select: [], 77 | join: [], 78 | orderBy: [], 79 | reset: function() { 80 | this.select = []; 81 | this.join = []; 82 | this.singleTable = ""; 83 | return this.orderBy = []; 84 | } 85 | }, 86 | SQLText: '', 87 | trimKeyWords: function(clause) { 88 | var patt; 89 | if (!clause) return ''; 90 | patt = /(SELECT|FROM|WHERE|ORDER BY|ORDER|BY)/gi; 91 | return $.trim(clause.replace(patt, '')); 92 | }, 93 | getSelectionOrder: function() { 94 | return ++this.curSelectionOrder; 95 | }, 96 | resetSelectionOrder: function() { 97 | return this.curSelectionOrder = 0; 98 | }, 99 | stripAllQuotes: function(str) { 100 | return str.replace(/'/g, ""); 101 | }, 102 | getSelectClause: function() { 103 | var patt, selColsPart, selectPart; 104 | patt = /SELECT.*FROM/i; 105 | selectPart = patt.exec(this.SQLText)[0]; 106 | selColsPart = this.trimKeyWords(selectPart); 107 | return $.trim(selColsPart); 108 | }, 109 | getJoinClause: function() { 110 | var joinPart, patt; 111 | patt = /FROM.*?(WHERE|ORDER)/i; 112 | patt = /FROM.*/i; 113 | joinPart = this.SQLText.match(patt)[0]; 114 | joinPart = joinPart.split(/(WHERE|ORDER)/i)[0]; 115 | return this.trimKeyWords(joinPart); 116 | }, 117 | getOrderByClause: function() { 118 | var patt; 119 | patt = /ORDER BY.*/i; 120 | return this.trimKeyWords((patt.exec(this.SQLText) || [''])[0]); 121 | }, 122 | getWhereClause: function() { 123 | var patt, wherePart; 124 | patt = /WHERE.*/i; 125 | wherePart = (patt.exec(this.SQLText) || [''])[0]; 126 | if (!wherePart) return 0; 127 | wherePart = wherePart.split('ORDER BY')[0]; 128 | return this.trimKeyWords(wherePart); 129 | }, 130 | parseJoinClause: function(joinClause) { 131 | var str; 132 | str = this.getJoinClause(); 133 | if (!str.match(/(JOIN)/i)) { 134 | this.SQLDataObject.singleTable = str; 135 | return; 136 | } 137 | this.resetSelectionOrder(); 138 | return this.parseNestedJoinClause(str); 139 | }, 140 | parseNestedJoinClause: function(joinClause) { 141 | var RightTblField, nestedExpr, oJoin, patt; 142 | joinClause = joinClause.slice(1, (joinClause.length - 1)); 143 | patt = /\(.*\)/gi; 144 | if (joinClause.match(patt)) { 145 | nestedExpr = joinClause.match(patt)[0]; 146 | RightTblField = this.parseNestedJoinClause(nestedExpr); 147 | joinClause = joinClause.replace(patt, RightTblField); 148 | } 149 | /* 150 | oJoin = @parseJoinCondition(joinClause) 151 | @SQLDataObject.join.push oJoin 152 | */ 153 | oJoin = this.parseJoinCondition(joinClause); 154 | oJoin.SelectionOrder = this.getSelectionOrder(); 155 | this.SQLDataObject.join.push(oJoin); 156 | return oJoin.RightTable; 157 | }, 158 | parseJoinCondition: function(joinExpr) { 159 | var arr, joinOnPart, joinType, patt, ret; 160 | joinExpr = joinExpr.replace(/\(|\)/g, ''); 161 | patt = /\S* \= \S*/g; 162 | joinOnPart = joinExpr.match(patt)[0]; 163 | patt = /\w{1,}/g; 164 | arr = joinOnPart.match(patt); 165 | ret = { 166 | LeftField: arr[1], 167 | LeftTable: arr[0], 168 | RightField: arr[3], 169 | RightTable: arr[2] 170 | }; 171 | patt = /(INNER|LEFT|RIGHT|CROSS)/i; 172 | joinType = joinExpr.match(patt)[0]; 173 | ret.Type = window.JoinDesc[joinType]; 174 | return ret; 175 | }, 176 | parseSelectClause: function() { 177 | var arrTblCol, i, selectPart, _i, _len; 178 | selectPart = this.getSelectClause(); 179 | arrTblCol = (function() { 180 | var _i, _len, _ref, _results; 181 | _ref = selectPart.split(','); 182 | _results = []; 183 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 184 | i = _ref[_i]; 185 | _results.push($.trim(i)); 186 | } 187 | return _results; 188 | })(); 189 | arrTblCol = (function() { 190 | var _i, _len, _results; 191 | _results = []; 192 | for (_i = 0, _len = arrTblCol.length; _i < _len; _i++) { 193 | i = arrTblCol[_i]; 194 | _results.push(this.parseSelectUnit(i)); 195 | } 196 | return _results; 197 | }).call(this); 198 | this.resetSelectionOrder(); 199 | for (_i = 0, _len = arrTblCol.length; _i < _len; _i++) { 200 | i = arrTblCol[_i]; 201 | i.SelectionOrder = this.getSelectionOrder(); 202 | } 203 | return this.SQLDataObject.select = arrTblCol; 204 | }, 205 | parseSelectUnit: function(unit) { 206 | var aliasName, cleanUnit, patt, ret, tableCol; 207 | patt = /\sAS\s/i; 208 | if (!unit.match(patt)) { 209 | return unit.split('.'); 210 | } else { 211 | cleanUnit = this.stripAllQuotes(unit); 212 | aliasName = cleanUnit.match(patt)[0]; 213 | aliasName = cleanUnit.split(' AS ')[1]; 214 | tableCol = cleanUnit.split(' AS ')[0]; 215 | aliasName = aliasName.replace(/'/g, ""); 216 | ret = tableCol.split('.'); 217 | ret.aliasName = aliasName; 218 | return ret; 219 | } 220 | }, 221 | parseOrderByClause: function() { 222 | /* 223 | res = [] 224 | (( j for j in i.split(/\.\s/g) ) for i in arr ) 225 | for i in arr ) 226 | */ 227 | var i; 228 | if (this.getOrderByClause() === "") return; 229 | return this.SQLDataObject.orderBy = (function() { 230 | var _i, _len, _ref, _results; 231 | _ref = this.getOrderByClause().split(','); 232 | _results = []; 233 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 234 | i = _ref[_i]; 235 | _results.push(this.parseOrderByUnit(i)); 236 | } 237 | return _results; 238 | }).call(this); 239 | }, 240 | parseOrderByUnit: function(unit) { 241 | var patt, ret; 242 | unit = $.trim(unit); 243 | ret = {}; 244 | /* 245 | if unit.match(/(DESC)/) 246 | ret.SortType = 'DESC' 247 | else 248 | ret.SortType = 'ASC' 249 | */ 250 | ret.SortType = unit.match(/(DESC)/) ? 'DESC' : 'ASC'; 251 | patt = /\w*\./; 252 | ret.TableName = unit.match(patt)[0].replace('.', ''); 253 | patt = /\.\w*/; 254 | ret.ColumnName = unit.match(patt)[0].replace('.', ''); 255 | return ret; 256 | } 257 | }; 258 | return window.SQLDataRestorer = { 259 | init: function(QID, sql) { 260 | this.QID = QID; 261 | this.mQuery = []; 262 | this.dQuery = []; 263 | if (this.QID > 0) { 264 | this.fetchServerData(); 265 | } else { 266 | this.mQuery.SQLText = sql; 267 | } 268 | this.mQuery.SQLText = this.mQuery.SQLText.replace(/(\r\n|\n|\r)/gm, ""); 269 | SQLParser.parseSQL(this.mQuery.SQLText); 270 | this.sqlData = SQLParser.SQLDataObject; 271 | return this.process(); 272 | }, 273 | fetchServerData: function() { 274 | this.mQuery = null; 275 | this.dQuery = null; 276 | this.mQuery = fetchTableResults('mQuery', this.QID)[0]; 277 | return this.dQuery = fetchTableResults('dQuery', this.QID); 278 | }, 279 | process: function() { 280 | var allTables, curX, curY, dX, dY, firstTFM, i, jDat, joinData, newX, newY, orderByData, selectData, storedTblData, tbl, tblNames, _i, _j, _k, _len, _len2, _len3; 281 | console.log("processing..."); 282 | selectData = this.sqlData.select; 283 | joinData = this.sqlData.join; 284 | orderByData = this.sqlData.orderBy; 285 | if (joinData.length > 0) { 286 | allTables = (function() { 287 | var _i, _len, _results; 288 | _results = []; 289 | for (_i = 0, _len = joinData.length; _i < _len; _i++) { 290 | i = joinData[_i]; 291 | _results.push([i.LeftTable, i.RightTable]); 292 | } 293 | return _results; 294 | })(); 295 | tblNames = _.uniq(_.flatten(allTables)); 296 | } else { 297 | tblNames = [this.sqlData.singleTable]; 298 | } 299 | for (_i = 0, _len = tblNames.length; _i < _len; _i++) { 300 | tbl = tblNames[_i]; 301 | AddTable(tbl); 302 | } 303 | /* 304 | for each table 305 | get x,y of first cell of a table 306 | get x,y of same cell from dQuery 307 | calc dx,dy 308 | set Table.chgX = dX 309 | set Table.chgY = dY 310 | */ 311 | if (this.dQuery.length !== 0) { 312 | for (_j = 0, _len2 = tblNames.length; _j < _len2; _j++) { 313 | tbl = tblNames[_j]; 314 | firstTFM = TableFields.getModelsByTableName(tbl)[0].toJSON(); 315 | curX = firstTFM.left; 316 | curY = firstTFM.top; 317 | storedTblData = _.find(this.dQuery, function(i) { 318 | return i.TableName === tbl && i.ColumnName === "*"; 319 | }); 320 | newX = storedTblData.OffSetX; 321 | newY = storedTblData.OffSetY; 322 | dX = newX - curX; 323 | dY = newY - curY; 324 | Tables.getModelByTableName(tbl).set_dx_dy(dX, dY).changeTopLeft(dX, dY).triggerChanges(); 325 | } 326 | } 327 | for (_k = 0, _len3 = joinData.length; _k < _len3; _k++) { 328 | jDat = joinData[_k]; 329 | console.log(jDat); 330 | TableFields.getModelByTableCol(jDat.LeftTable, jDat.LeftField).chgJoinLeft(); 331 | TableFields.getModelByTableCol(jDat.RightTable, jDat.RightField).chgJoinRight(); 332 | Joins.last().set({ 333 | Type: jDat.Type 334 | }); 335 | } 336 | /* 337 | select clause 338 | */ 339 | _.each(selectData, function(item) { 340 | var mdl; 341 | mdl = TableFields.getModelByTableCol(item[0], item[1]); 342 | mdl.toggleSelected(); 343 | if (item.aliasName) { 344 | return mdl.set({ 345 | Alias: item.aliasName 346 | }); 347 | } 348 | }); 349 | SQLParser.resetSelectionOrder(); 350 | _.each(orderByData, function(item) { 351 | var mdl; 352 | mdl = TableFields.getModelByTableCol(item.TableName, item.ColumnName); 353 | mdl.toggleSort(); 354 | mdl.set({ 355 | SortOrder: SQLParser.getSelectionOrder() 356 | }); 357 | if (item.SortType === 'DESC') { 358 | return mdl.set({ 359 | Sort: 'DESC' 360 | }); 361 | } 362 | }); 363 | return this.parseNestedWhereClause(); 364 | }, 365 | parseNestedWhereClause: function() { 366 | var wherePart; 367 | wherePart = SQLParser.getWhereClause(); 368 | if (!wherePart) return 0; 369 | return this.parseWhereClause(wherePart); 370 | }, 371 | parseWhereClause: function(whereExpr) { 372 | var i, patt, _i, _len, _ref, _results; 373 | whereExpr = whereExpr.slice(1, (whereExpr.length - 1)); 374 | patt = /\(.*?\)/gi; 375 | if ((whereExpr.match(patt) || [false])[0]) { 376 | _ref = whereExpr.match(patt); 377 | _results = []; 378 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 379 | i = _ref[_i]; 380 | _results.push(this.parseWhereClause(i)); 381 | } 382 | return _results; 383 | } else { 384 | return this.parseWhereCondition(whereExpr); 385 | } 386 | }, 387 | parseWhereCondition: function(expr) { 388 | var colName, leftOprnd, optr, patt, rightOprnd, tableFld, tblName, tokens; 389 | console.log('inside parseWhereCondition...'); 390 | expr = expr.replace(/\(|\)/g, ''); 391 | tokens = expr.split(" "); 392 | leftOprnd = tokens[0]; 393 | optr = tokens[1]; 394 | if (optr === 'LIKE') { 395 | patt = /'.*'/; 396 | rightOprnd = expr.match(patt)[0]; 397 | rightOprnd = rightOprnd.replace(/'/g, ""); 398 | } else { 399 | rightOprnd = tokens[2]; 400 | } 401 | tblName = leftOprnd.split(".")[0]; 402 | colName = leftOprnd.split(".")[1]; 403 | tableFld = TableFields.getModelByTableCol(tblName, colName); 404 | return BoolExprs.create({ 405 | LeftTableField: tableFld, 406 | Operator: optr, 407 | RightOperand: rightOprnd 408 | }); 409 | } 410 | }; 411 | }); 412 | -------------------------------------------------------------------------------- /coffee/watch.sh: -------------------------------------------------------------------------------- 1 | pwd 2 | coffee -bwc *.coffee 3 | 4 | -------------------------------------------------------------------------------- /conn.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /css/design.css: -------------------------------------------------------------------------------- 1 | body { 2 | /*font-family: "Open Sans", arial, sans-serif;*/ 3 | font-family: arial, sans-serif; 4 | background-color: #FFFFFF; 5 | font-size: 14px; 6 | } 7 | 8 | select { 9 | border-width: 0; 10 | background-color: #EDEDED; 11 | font-size: 10pt; 12 | padding: 0 2px; 13 | outline: 1px solid #ADADAD; 14 | } 15 | 16 | pre { 17 | font-family: Consolas, monospace; 18 | } 19 | 20 | #content { 21 | z-index: 0; 22 | } 23 | 24 | #project-logo { 25 | background-color: #6165A5; 26 | padding: 3px 5px; 27 | /*border: 1px solid #88A8E0;*/ 28 | color: #fff; 29 | cursor: default; 30 | font-weight: bold; 31 | } 32 | 33 | #project-logo .wb { 34 | opacity: 0.8; 35 | } 36 | 37 | #project-logo .vqd { 38 | opacity: 1; 39 | 40 | } 41 | #right-pane { 42 | border: 1px solid #B2B2B2; 43 | background-color: #F4F4F4; 44 | padding: 5px 10px; 45 | 46 | } 47 | 48 | #design-pane { 49 | height: 200px; 50 | border: 1px solid #A3A3A3; 51 | background-color: #FFFFFF; 52 | margin-top: 10px; 53 | position: relative; 54 | overflow: scroll; 55 | } 56 | 57 | .pane { 58 | border: 1px solid #B2B2B2; 59 | border-top-color:#A3A3A3 ; 60 | border-bottom-color: #EFEFEF ; 61 | padding: 5px; 62 | font-size: 14px; 63 | margin-top: 10px; 64 | background-color: #D6D6D6; 65 | 66 | } 67 | 68 | .line-item { 69 | overflow: auto; 70 | border-bottom: 1px solid #C1C1C1; 71 | background-color: #F9F9F9; 72 | /*background-color: #F2F2F2;*/ 73 | padding: 1px 3px 0; 74 | cursor: move; 75 | } 76 | 77 | .hidden-item { 78 | display:none; 79 | } 80 | 81 | .line-item > div { 82 | float: left; 83 | 84 | } 85 | .icon { 86 | height: 15px; 87 | width: 15px; 88 | margin: 4px 1px 0 1px; 89 | cursor: pointer; 90 | } 91 | 92 | .line-item .icon { 93 | opacity: 0.0; 94 | 95 | } 96 | 97 | .line-item .icon:hover { 98 | opacity: 1.0 !important; 99 | 100 | } 101 | .line-item:hover .icon { 102 | opacity: 0.4; 103 | 104 | } 105 | .icon.delete { 106 | background-image: url('.././images/icons/cross.png'); 107 | margin-right: 5px; 108 | } 109 | 110 | .icon.rearrange { 111 | background-image: url('.././images/icons/up-down16.png'); 112 | } 113 | .line-item .property-editor { 114 | margin-left: 5px; 115 | font-size: 12px; 116 | float: none; 117 | overflow: auto; 118 | } 119 | .property-editor > div { 120 | float:left; 121 | height: inherit; 122 | font-size: 14px; 123 | } 124 | .property-editor .field-name { 125 | min-width: 100px; 126 | height: inherit; 127 | padding-top: 2px; 128 | font-size: 10pt; 129 | } 130 | 131 | .diag-table { 132 | outline: 1px dotted #84FF8C; 133 | float: left; 134 | z-index: 10; 135 | } 136 | 137 | .diag-table > div { 138 | height: 20px; /* must match with those of .diag-cell*/ 139 | width: 140px; 140 | } 141 | 142 | .diag-table > .label { 143 | /*background-color: #FFF3A5;*/ 144 | /*background-color: #A5D3FF;*/ 145 | background-color: #8EB6DB; 146 | color: #ffffff; 147 | /*padding: 2px 3px;*/ 148 | } 149 | 150 | .diag-cell { 151 | height: 18px; /* must match with those of .diag-cell*/ 152 | min-width: 180px; 153 | /*outline: 1px solid #4074CE;*/ 154 | border: 1px solid #4074CE; 155 | border-top: 0; 156 | border-bottom: 0; 157 | background-color: #C5E0F7; 158 | z-index: 10; 159 | left:0; 160 | top:0; 161 | opacity: 0.85; 162 | font-size: 13px; 163 | padding-bottom: 1px; 164 | } 165 | 166 | .diag-cell.label { 167 | text-align: center; 168 | /*background-color: #496FB7;*/ 169 | 170 | border-color: #2D5AA8; 171 | background-color: #182E4F; 172 | font-weight: bold; 173 | color: #FFF; 174 | cursor: move; 175 | font-size: 13px; 176 | } 177 | 178 | /*???*/ 179 | .diag-join { 180 | z-index: 0; 181 | } 182 | .primary-key { 183 | font-weight: bold; 184 | } 185 | #sql-out.input-mode .output { 186 | display:none; 187 | } 188 | #sql-out.output-mode .input { 189 | display:none; 190 | } 191 | #sql-out .output { 192 | border-bottom: 1px solid #B5B7B2; 193 | background-color: #F9F9F9; 194 | font-family: monospace; 195 | padding: 2px; 196 | } 197 | 198 | .operator { 199 | margin: 3px 10px; 200 | } 201 | 202 | #pane-select .alias { 203 | width: 100px; 204 | } 205 | 206 | .inline-pane { 207 | float: left; 208 | width: 30%; 209 | overflow: auto ; 210 | } 211 | /* 212 | .inline-pane > fieldset { 213 | height: 50%; 214 | } 215 | */ 216 | 217 | legend { 218 | border:1px solid #A0A0A0; 219 | background-color: #FFFFFF; 220 | color: #565656; 221 | font-size: 10pt; 222 | font-weight: bolder; 223 | padding: 1px 5px; 224 | } 225 | /**/ 226 | /*input.alias {*/ 227 | /* height: 14px;*/ 228 | /*}*/ 229 | .btn-refresh { 230 | padding: 3px 5px; 231 | background-color: #F2DCAB; 232 | border: 1px solid #BC811A; 233 | font-weight: bold; 234 | color: #663B0B; 235 | } 236 | 237 | .btn-refresh:hover { 238 | background-color: #A0866A; 239 | color: #F2DCAB; 240 | 241 | 242 | } 243 | 244 | .cell-dragger > .cell-edit { 245 | float: right; 246 | /*width:10px;*/ 247 | /*margin-top: 2px;*/ 248 | margin: 0 5px 0 0; 249 | 250 | } 251 | .hoverable { 252 | opacity:0.2; 253 | cursor: pointer; 254 | } 255 | .cell-dragger:hover .hoverable { 256 | opacity:0.7; 257 | } 258 | 259 | .property-editor span.label { 260 | opacity: 0.7; 261 | font-size: smaller; 262 | 263 | } 264 | 265 | .property-editor .editable { 266 | margin-left: 5px; 267 | float: right; 268 | margin: 0 5px; 269 | } 270 | #tool-bar .tool-bar-btn { 271 | float:left; 272 | cursor: pointer; 273 | padding: 2px 4px; 274 | margin-left: 10px; 275 | 276 | background: #C07272; 277 | color: #FFFFFF; 278 | border: 1px solid #494949; 279 | } 280 | 281 | #tool-bar { 282 | overflow: auto; 283 | } 284 | .design-mode .pane { 285 | display: block; 286 | } 287 | 288 | .diag-cell.label .close { 289 | float:right; 290 | padding:2px; 291 | } 292 | 293 | .diag-cell.label .close:hover { 294 | background-color: #E8EFFC; 295 | color: #6C85B5; 296 | cursor: pointer; 297 | } 298 | 299 | /*.diag-cell {*/ 300 | /* opacity: 0.8;*/ 301 | /*}*/ 302 | 303 | .orderby { 304 | background: #4362A5; 305 | color: #fff; 306 | font-weight: bold; 307 | cursor: pointer; 308 | padding: 0 3px; 309 | } 310 | 311 | /*.cell-edit {*/ 312 | /* margin-top: 2px;*/ 313 | /*}*/ 314 | /**/ 315 | #table-list { 316 | overflow-x: auto; 317 | overflow-y: scroll; 318 | max-height: 300px; 319 | 320 | } 321 | 322 | #sql-text-op { 323 | padding-left: 10px; 324 | font-size: 12px; 325 | } 326 | 327 | .diag-cell .field-name { 328 | /*overflow-x: hidden;*/ 329 | font-family: Tahoma,sans-serif; 330 | opacity: 1; 331 | /*font-size: 12px;*/ 332 | } 333 | 334 | .diag-cell input[type="checkbox"] { 335 | margin: 2px 1px 0 3px !important; 336 | } 337 | /* 338 | #pane-where { 339 | width:20%; 340 | }*/ 341 | 342 | #pane-where { 343 | width: auto; 344 | } 345 | #pane-where textarea { 346 | border: none; 347 | border: 1px solid #7A7A7A; 348 | } 349 | 350 | .breadcrumbs-menu { 351 | position:absolute; 352 | z-index: 10; 353 | border: 2px solid #F4D753; 354 | text-align: center; 355 | margin-left: 40%; 356 | background-color: #FFFA72; 357 | padding: 3px 2px; 358 | color: #EA4A00; 359 | font-size: 16px; 360 | /*margin-top: -5px;*/ 361 | top: 3px; 362 | opacity: 0.7; 363 | } 364 | .breadcrumbs-menu a { 365 | color: inherit; 366 | } 367 | .breadcrumbs-menu .current { 368 | font-weight: bolder; 369 | } 370 | 371 | #table-selection { 372 | opacity: 0.9; 373 | } 374 | 375 | #feedback-btn { 376 | float: right; 377 | padding: 1px; 378 | padding: 0px 2px; 379 | color: black; 380 | background: white; 381 | border: 2px dashed; 382 | margin: 0px 2px; 383 | 384 | } 385 | -------------------------------------------------------------------------------- /css/tbl_list_overlay.css: -------------------------------------------------------------------------------- 1 | 2 | #table-list { 3 | /*width:300px;*/ 4 | font-family : sans-serif; 5 | margin: 0.5em 0; 6 | 7 | } 8 | #table-list .item { 9 | border: 1px solid #D8D8D8; 10 | cursor: pointer; 11 | } 12 | 13 | #table-list .selected { 14 | background-color: #FFEA8E; 15 | } 16 | 17 | #table-list .added { 18 | background-color: #CECECE; 19 | } 20 | 21 | #table-btns { 22 | margin-top: 10px; 23 | } 24 | 25 | #table-btns .butn { 26 | background-color: #ADCFFF; 27 | margin-right: 10px; 28 | cursor: pointer; 29 | padding: 2px 4px; 30 | } 31 | 32 | #table-selection { 33 | position: fixed; 34 | top: 30%; 35 | left: 30%; 36 | z-index: 10000; 37 | background-color: #ffffff; 38 | padding: 10px; 39 | border: 5px solid #C4C4C4; 40 | 41 | margin-top: 20px; 42 | margin-bottom: 20px; 43 | } 44 | -------------------------------------------------------------------------------- /images/icons/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swapnilmj/web-vqd/4177ca1c325a6448cc66d96c0dbf5e80c21c57d8/images/icons/cross.png -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 36 | 41 | 42 | 43 | 44 |122 | Select a schema and then add tables/views. 123 |124 |
=64){y[0]=(E[0].x+E[D].x)/2;return 1}var s,r,p;v=E[0].y-E[D].y;w=E[D].x-E[0].x;A=E[0].x*E[D].y-E[D].x*E[0].y;t=max_distance_below=0;for(r=1;r