SQL在线查询设计器

简介: SQL在线查询设计器
//http://www.html580.com
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTableSprite', {
    extend: 'Ext.draw.Sprite',
    alias: ['widget.sqltablesprite'],
    bConnections: false,
    startDrag: function(id){
        var me = this, win, sqlTablePanel, xyParentPos, xyChildPos;
        
        // get a reference to a sqltable
        win = Ext.getCmp(id);
        
        // get the main sqlTablePanel
        sqlTablePanel = Ext.getCmp('SQLTablePanel');
        
        // get the main sqlTablePanel position
        xyParentPos = sqlTablePanel.el.getXY();
        
        // get the size of the previously added sqltable
        xyChildPos = win.el.getXY();
        
        me.prev = me.surface.transformToViewBox(xyChildPos[0] - xyParentPos[0] + 2, xyChildPos[1] - xyParentPos[1] + 2);
    },
    
    onDrag: function(relPosMovement){
        var xy, me = this, attr = this.attr, newX, newY;
        // move the sprite
        // calculate new x and y position
        newX = me.prev[0] + relPosMovement[0];
        newY = me.prev[1] + relPosMovement[1];
        // set new x and y position and redraw sprite
        me.setAttributes({
            x: newX,
            y: newY
        
        }, true);
    }
}); 
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTableModel', {
    extend: 'Ext.data.Model',
    fields: [{
        name: 'id',
        type: 'string'
    }, {
        name: 'tableName',
        type: 'string'
    }, {
        name: 'tableAlias',
        type: 'string'
    }]
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTableStore', {
    extend: 'Ext.data.Store',
    autoSync: true,
    model: 'Ext.ux.window.visualsqlquerybuilder.SQLTableModel',
    proxy: {
        type: 'memory'
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLJoin', {
    extend: 'Ext.data.Model',
    fields: [{
        name: 'id',
        type: 'string'
    }, {
        name: 'leftTableId',
        type: 'string'
    }, {
        name: 'rightTableId',
        type: 'string'
    }, {
        name: 'leftTableField',
        type: 'string'
    }, {
        name: 'rightTableField',
        type: 'string'
    }, {
        name: 'joinCondition',
        type: 'string'
    }, {
        name: 'joinType',
        type: 'string'
    }],
    createUUID: function(){
        // http://www.ietf.org/rfc/rfc4122.txt
        var s = [];
        var hexDigits = "0123456789abcdef";
        for (var i = 0; i < 36; i++) {
            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
        }
        s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
        s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
        s[8] = s[13] = s[18] = s[23] = "-";
        
        var uuid = s.join("");
        return uuid;
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.JoinStore', {
    extend: 'Ext.data.Store',
    autoSync: true,
    model: 'Ext.ux.window.visualsqlquerybuilder.SQLJoin',
    proxy: {
        type: 'memory'
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLFieldsModel', {
    extend: 'Ext.data.Model',
    fields: [{
        name: 'id',
        type: 'string'
    }, {
        name: 'tableName',
        type: 'string'
    }, {
        name: 'tableId',
        type: 'string'
    }, {
        name: 'extCmpId',
        type: 'string'
    }, {
        name: 'tableAlias',
        type: 'string'
    }, {
        name: 'field',
        type: 'string'
    }, {
        name: 'output',
        type: 'boolean'
    }, {
        name: 'expression',
        type: 'string'
    }, {
        name: 'aggregate',
        type: 'string'
    }, {
        name: 'alias',
        type: 'string'
    }, {
        name: 'sortType',
        type: 'string'
    }, {
        name: 'sortOrder',
        type: 'int'
    }, {
        name: 'grouping',
        type: 'boolean'
    }, {
        name: 'criteria',
        type: 'string'
    }]
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLFieldsStore', {
    extend: 'Ext.data.Store',
    autoSync: true,
    model: 'Ext.ux.window.visualsqlquerybuilder.SQLFieldsModel',
    proxy: {
        type: 'memory'
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLSelect', {
    config: {
        tables: '',
        fields: '',
        joins: ''
    },
    constructor: function(){
    
        this.tables = Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLTableStore', {
            storeId: 'SQLTableStore'
        });
        
        // handle all updates on sql tables
        this.tables.on('update', this.handleSQLTableUpdate, this);
        this.tables.on('add', this.handleSQLTableAdd, this);
        this.tables.on('remove', this.handleSQLTableRemove, this);
        
        this.fields = Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLFieldsStore', {
            storeId: 'SQLFieldsStore'
        });
        
        this.fields.on('update', this.handleSQLFieldChanges, this);
        this.fields.on('remove', this.handleSQLFieldRemove, this);
        
        this.joins = Ext.create('Ext.ux.window.visualsqlquerybuilder.JoinStore', {
            storeId: 'JoinStore'
        });
        
        // this.joins.on('update', this.handleSQLJoinChanges, this);
        this.joins.on('add', this.handleSQLJoinChanges, this);
        this.joins.on('remove', this.handleSQLJoinChanges, this);
        
        this.callParent(arguments);
    },
    handleSQLTableUpdate: function(tableStore, table, operation){
        if (operation == 'commit') {
            this.updateFieldTableData(table);
            this.updateJoinTableData(table);
            this.updateSQLOutput();
        }
    },
    handleSQLTableAdd: function(tableStore, table, index){
        this.updateSQLOutput();
    },
    handleSQLTableRemove: function(tableStore, table, index){
        var aJoins = [];
        // get table joins and remove them
        aJoins = this.getJoinsByTableId(table.get('id'));
        // loop over the joins array
        for (var i = 0, l = aJoins.length; i < l; i++) {
            // remove join from store
            this.removeJoinById(aJoins[i].get('id'));
        }
        this.updateSQLOutput();
    },
    handleSQLJoinChanges: function(joinStore, join){
        this.updateSQLOutput();
    },
    updateFieldTableData: function(table){
        var tableId, expression, tableAlias, tableName;
        tableId = table.get('id');
        tableAlias = table.get('tableAlias');
        tableName = table.get('tableName');
        // loop over all fields of the fields store
        this.fields.each(function(field){
            // check if current field belongs to sql table
            if (field.get('tableId') == tableId) {
                if (tableAlias != '') {
                    // we have a table alias
                    expression = tableAlias + '.' + field.get('field');
                }
                else {
                    // no table alias
                    expression = tableName + '.' + field.get('field');
                };
                field.beginEdit();
                // update the field table alias
                field.set('tableAlias', tableAlias);
                // update the field expression
                field.set('expression', expression);
                field.commit(true);
                field.endEdit();
            }
        });
        return;
    },
    updateJoinTableData: function(table){
        var joins, tableId;
        tableId = table.get('id');
        joins = this.getJoinsByTableId(tableId);
        for (var i = 0, rightTable, leftTable, joinCondition = '',l = joins.length; i < l; i++) {
            leftTable = this.getTableById(joins[i].get('leftTableId'));
            rightTable = this.getTableById(joins[i].get('rightTableId'));
            
            if (leftTable.get('tableAlias') != '') {
                joinCondition = joinCondition + leftTable.get('tableAlias') + '.' + joins[i].get('leftTableField') + '=';
            }
            else {
                joinCondition = joinCondition + leftTable.get('tableName') + '.' + joins[i].get('leftTableField') + '=';
            }
            
            if (rightTable.get('tableAlias') != '') {
                joinCondition = joinCondition + rightTable.get('tableAlias') + '.' + joins[i].get('rightTableField');
            }
            else {
                joinCondition = joinCondition + rightTable.get('tableName') + '.' + joins[i].get('rightTableField');
            }
            joins[i].beginEdit();
            joins[i].set('joinCondition', joinCondition);
            joins[i].commit(true);
            joins[i].endEdit();
        }
    },
    handleSQLFieldChanges: function(fieldStore, model, operation){
        if (operation == 'commit') {
            this.updateSQLOutput();
        }
    },
    handleSQLFieldRemove: function(fieldStore){
        this.updateSQLOutput();
    },
    updateSQLOutput: function(){
        var sqlOutput, sqlHTML, sqlQutputPanel;
        sqlOutput = this.toString();
        sqlHTML = '<pre class="brush: sql">' + sqlOutput + '</pre>';
        sqlQutputPanel = Ext.getCmp('SQLOutputPanel');
        
        sqlQutputPanel.update(sqlHTML);
    },
    sortTablesByJoins: function(tables, oUsedTables){
        var aTables = [], aJoins = [], oUsedTables = oUsedTables ||
        {};
        // loop over tables
        for (var i = 0, aCondition = [], aJoin, l = tables.length; i < l; i++) {
            // check if current table is a new one
            if (!oUsedTables.hasOwnProperty(tables[i].get('id'))) {
                // it is a new one
                aTables.push(tables[i]);
                // mark table as used
                oUsedTables[tables[i].get('id')] = true;
                // get any joins for the current table
                aJoin = this.getJoinsByTableId(tables[i].get('id'));
                // loop over the join tables
                for (var j = 0, joinTable, len = aJoin.length; j < len; j++) {
                    // check if it is a new join
                    if (!oUsedTables.hasOwnProperty(aJoin[j].get('id'))) {
                        // mark join as used
                        oUsedTables[aJoin[j].get('id')] = true;
                        if (tables[i].get('id') != aJoin[j].get('leftTableId')) {
                            joinTable = this.getTableById(aJoin[j].get('leftTableId'));
                            this.changeLeftRightOnJoin(aJoin[j]);
                        }
                        else {
                            joinTable = this.getTableById(aJoin[j].get('rightTableId'));
                        }
                        oTemp = this.sortTablesByJoins([joinTable], oUsedTables);
                        oUsedTables = oTemp.oUsedTables;
                        aTables = aTables.concat(oTemp.aTables);
                    }
                }
            }
        }
        
        return {
            aTables: aTables,
            oUsedTables: oUsedTables
        };
    },
    changeLeftRightOnJoin: function(join){
        var leftTable, leftTableField, rightTable, rightTableField, joinCondition = '';
        // prepare new data
        leftTable = this.getTableById(join.get('rightTableId'));
        leftTableField = join.get('rightTableField');
        rightTable = this.getTableById(join.get('leftTableId'));
        rightTableField = join.get('leftTableField');
        
        // construct new joinCondition
        if (leftTable.get('tableAlias') != '') {
            joinCondition = joinCondition + leftTable.get('tableAlias') + '.' + join.get('rightTableField') + '=';
        }
        else {
            joinCondition = joinCondition + leftTable.get('tableName') + '.' + join.get('rightTableField') + '=';
        }
        
        if (rightTable.get('tableAlias') != '') {
            joinCondition = joinCondition + rightTable.get('tableAlias') + '.' + join.get('leftTableField');
        }
        else {
            joinCondition = joinCondition + rightTable.get('tableName') + '.' + join.get('leftTableField');
        }
        
        // start transaction
        join.beginEdit();
        // change left and right join table data
        join.set('leftTableId', leftTable.get('id'));
        join.set('leftTableField', leftTableField);
        join.set('rightTableId', rightTable.get('id'));
        join.set('rightTableField', rightTableField);
        join.set('joinCondition', joinCondition);
        // silent commit without firing store events
        // this prevents endless loop
        join.commit(true);
        join.endEdit();
        // end transaction
        return;
    },
    toString: function(){
        var sqlOutput = 'SELECT ', aJoins = [], aOutputFields = [], oJoinTables = {}, aTables = [], aJoinTables = [], aCriteriaFields = [], aGroupFields = [], aOrderFields = [], selectFieldsSQL = '', fromSQL = '', aFromSQL = [], criteriaSQL = '', orderBySQL = '', groupBySQL = '', fieldSeperator = ', ', joinSQL = '', bFirst = true, bPartOfJoin = false;
        this.fields.each(function(field){
            // should the field be a part of the output
            if (field.get('output')) {
                aOutputFields.push(field);
            }
            // any criteria
            if (field.get('criteria') != '') {
                aCriteriaFields.push(field);
            }
            // check for grouping
            if (field.get('grouping')) {
                aGroupFields.push(field);
            }
            // check for sorting
            if (field.get('sortType') != '') {
                aOrderFields.push(field);
            }
        });
        
        // tables
        // sorting of tables
        this.tables.each(function(table){
            aTables.push(table);
        });
        
        aTables = this.sortTablesByJoins(aTables).aTables;
        
        
        this.joins.each(function(join){
            aJoins.push(join);
        });
        
        //create fromSQL
        for (var k = 0, aJoin = [], oJoinTables = {}, joinCondition = '', joinType, leftTable, rightTable, l = aTables.length; k < l; k++) {
            if (k == aTables.length - 1) {
                fieldSeperator = '';
            }
            else {
                fieldSeperator = ', ';
            };
            
            // is the current table the first one
            if (bFirst) {
                // yes it is the first
                
                // table id merken
                oJoinTables[aTables[k].get('id')] = true;
                
                bFirst = false;
                
                // check if current table is not the last one in the loop 
                if ((k + 1) < aTables.length) {
                    // get joins where joins leftTableID is a property of oJoinTables and joins rightTableID equal to aTables[i+1].get('id')
                    for (var h = 0, len = aJoins.length; h < len; h++) {
                        if (oJoinTables.hasOwnProperty(aJoins[h].get('leftTableId')) && aJoins[h].get('rightTableId') == aTables[k + 1].get('id')) {
                            aJoin.push(aJoins[h]);
                        }
                        if (oJoinTables.hasOwnProperty(aJoins[h].get('rightTableId')) && aJoins[h].get('leftTableId') == aTables[k + 1].get('id')) {
                            this.changeLeftRightOnJoin(aJoins[h]);
                            aJoin.push(aJoins[h]);
                        }
                    }
                    
                    // check if we have a join
                    if (aJoin.length > 0) {
                        // yes we have a join between aTables[k] and aTables[k+1] with at least one join condition
                        
                        leftTable = aTables[k];
                        rightTable = aTables[k + 1];
                        
                        // table id merken
                        oJoinTables[rightTable.get('id')] = true;
                        
                        for (var j = 0, fieldSeperator = '', ln = aJoin.length; j < ln; j++) {
                            if (j == aJoin.length - 1) {
                                fieldSeperator = '';
                            }
                            else {
                                fieldSeperator = '\nAND ';
                            };
                            joinType = aJoin[j].get('joinType');
                            joinCondition = joinCondition + aJoin[j].get('joinCondition') + fieldSeperator;
                        }
                        
                        // reset the join array 
                        aJoin = [];
                        
                        if (joinSQL != '') {
                            joinSQL = joinSQL + ',\n';
                        }
                        
                        if (leftTable.get('tableAlias') != '') {
                            // we have an leftTable alias
                            joinSQL = joinSQL + leftTable.get('tableName') + ' ' + leftTable.get('tableAlias') + ' ' + joinType + ' JOIN ';
                        }
                        else {
                            //no alias
                            joinSQL = joinSQL + leftTable.get('tableName') + ' ' + joinType + ' JOIN ';
                        }
                        
                        if (rightTable.get('tableAlias') != '') {
                            // we have an rightTable alias
                            joinSQL = joinSQL + rightTable.get('tableName') + ' ' + rightTable.get('tableAlias') + ' ON ' + joinCondition;
                        }
                        else {
                            //no alias
                            joinSQL = joinSQL + rightTable.get('tableName') + ' ON ' + joinCondition;
                        }
                        
                        // clear joinCondition
                        joinCondition = '';
                        
                    }
                    else {
                        // no join between aTables[i+1] and the one before
                        bFirst = true;
                        oJoinTables = {};
                        // check for tableAlias
                        if (aTables[k].get('tableAlias') != '') {
                            fromSQL = aTables[k].get('tableName') + ' ' + aTables[k].get('tableAlias');
                        }
                        else {
                            fromSQL = aTables[k].get('tableName');
                        }
                        aFromSQL.push(fromSQL);
                    }
                }
                else {
                    // its the last and only one in the loop
                    // check for tableAlias
                    if (aTables[k].get('tableAlias') != '') {
                        fromSQL = aTables[k].get('tableName') + ' ' + aTables[k].get('tableAlias');
                    }
                    else {
                        fromSQL = aTables[k].get('tableName');
                    }
                    aFromSQL.push(fromSQL);
                }
            }
            else {
                // no, it is not the first table
                
                bFirst = true;
                
                // check if current table is not the last one in the loop 
                if ((k + 1) < aTables.length) {
                    // get joins where joins leftTableID is a property of oJoinTables and joins rightTableID equal to aTables[i+1].get('id')
                    for (var h = 0, len = aJoins.length; h < len; h++) {
                        if (oJoinTables.hasOwnProperty(aJoins[h].get('leftTableId')) && aJoins[h].get('rightTableId') == aTables[k + 1].get('id')) {
                            aJoin.push(aJoins[h]);
                        }
                        if (oJoinTables.hasOwnProperty(aJoins[h].get('rightTableId')) && aJoins[h].get('leftTableId') == aTables[k + 1].get('id')) {
                            this.changeLeftRightOnJoin(aJoins[h]);
                            aJoin.push(aJoins[h]);
                        }
                    }
                    
                    // check if we have a join
                    if (aJoin.length > 0) {
                        // yes we have a join between aTables[k] and aTables[k+1] with at least one join condition
                        
                        rightTable = aTables[k + 1];
                        
                        // table id merken
                        oJoinTables[rightTable.get('id')] = true;
                        
                        for (var j = 0, fieldSeperator = '', ln = aJoin.length; j < ln; j++) {
                            if (j == aJoin.length - 1) {
                                fieldSeperator = '';
                            }
                            else {
                                fieldSeperator = '\nAND ';
                            };
                            joinType = aJoin[j].get('joinType');
                            joinCondition = joinCondition + aJoin[j].get('joinCondition') + fieldSeperator;
                        }
                        
                        // reset the join array 
                        aJoin = [];
                        
                        bFirst = false;
                        
                        if (rightTable.get('tableAlias') != '') {
                            // we have an rightTable alias
                            joinSQL = joinSQL + '\n' + joinType + ' JOIN ' + rightTable.get('tableName') + ' ' + rightTable.get('tableAlias') + ' ON ' + joinCondition;
                        }
                        else {
                            //no alias
                            joinSQL = joinSQL + '\n' + joinType + ' JOIN ' + rightTable.get('tableName') + ' ON ' + joinCondition;
                        }
                        
                        // clear joinCondition
                        joinCondition = '';
                    }
                    else {
                        bFirst = true;
                        oJoinTables = {};
                    }
                }
                else {
                    // its the last and only one
                    // check for tableAlias
                    oJoinTables = {};
                }
            }
        }
        
        fromSQL = aFromSQL.join(', ');
        
        if (joinSQL != '' && fromSQL != '') {
            joinSQL = joinSQL + ', ';
        }
        
        fromSQL = '\nFROM ' + joinSQL + fromSQL;
        
        // output fields
        for (var i = 0, l = aOutputFields.length; i < l; i++) {
            // check if it is the last array member
            if (i == aOutputFields.length - 1) {
                fieldSeperator = '';
            }
            else {
                fieldSeperator = ', ';
            };
            // yes, output
            // check alias
            if (aOutputFields[i].get('alias') != '') {
                // yes, we have an field alias
                selectFieldsSQL = selectFieldsSQL + aOutputFields[i].get('expression') + ' AS ' + aOutputFields[i].get('alias') + fieldSeperator;
            }
            else {
                // no field alias
                selectFieldsSQL = selectFieldsSQL + aOutputFields[i].get('expression') + fieldSeperator;
            }
        }
        
        // criteria
        for (var i = 0, l = aCriteriaFields.length; i < l; i++) {
            if (i == 0) {
                criteriaSQL = criteriaSQL + '\nWHERE ';
            }
            else {
                criteriaSQL = criteriaSQL + 'AND ';
            }
            if (i == aCriteriaFields.length - 1) {
                fieldSeperator = '';
            }
            else {
                fieldSeperator = '\n';
            }
            criteriaSQL = criteriaSQL + aCriteriaFields[i].get('expression') + ' ' + aCriteriaFields[i].get('criteria') + fieldSeperator;
        }
        
        // group by
        for (var i = 0, l = aGroupFields.length; i < l; i++) {
            // check if it is the last array member
            if (i == aGroupFields.length - 1) {
                fieldSeperator = '';
            }
            else {
                fieldSeperator = ', ';
            }
            if (i == 0) {
                groupBySQL = '\nGROUP BY ';
            }
            groupBySQL = groupBySQL + aGroupFields[i].get('expression') + fieldSeperator;
        }
        
        // order by
        for (var i = 0, l = aOrderFields.length; i < l; i++) {
            // check if it is the last array member
            if (i == aOrderFields.length - 1) {
                fieldSeperator = '';
            }
            else {
                fieldSeperator = ', ';
            }
        }
        
        return sqlOutput + selectFieldsSQL + fromSQL + criteriaSQL + groupBySQL + orderBySQL;
    },
    getJoinsByTableId: function(tableId){
        var aReturn = [];
        this.joins.each(function(join){
            if (join.get('leftTableId') == tableId || join.get('rightTableId') == tableId) {
                aReturn.push(join);
            }
        });
        return aReturn;
    },
    removeTableById: function(tableID){
        var table;
        table = this.tables.getById(tableID);
        this.tables.remove(table);
    },
    getTableById: function(tableID){
        return this.tables.getById(tableID);
    },
    removeFieldById: function(id){
        var field;
        field = this.fields.getById(id);
        this.fields.remove(field);
    },
    removeFieldsByTableId: function(tableId){
        var aRecords = [];
        this.fields.each(function(model){
            if (model.get('tableId') == tableId) {
                aRecords.push(model);
            }
        });
        this.fields.remove(aRecords);
    },
    addTable: function(table){
        this.tables.add(table);
    },
    addFieldRecord: function(record, bOutput){
        var tableAlias, model, expression;
        // get the tableAlias
        tableAlias = this.getTableById(record.get('tableId')).get('tableAlias');
        // build the expression
        // check if the tableAlias is not an empty string
        if (tableAlias != '') {
            // alias is not an empty string
            expression = tableAlias + '.' + record.get('field');
        }
        else {
            // alias is an empty string
            expression = record.get('tableName') + '.' + record.get('field');
        };
        // get a new field instance
        model = this.getNewField();
        // set the expression
        model.set('expression', expression);
        // set output to false per default
        model.set('output', bOutput);
        // set an id, so it is possible to remove rows if the associated table is removed
        model.set('id', record.get('id'));
        // set the field
        model.set('field', record.get('field'));
        // copy tableId to the new model instance
        model.set('tableId', record.get('tableId'));
        // copy cmp id of origin sqltable to the new model instance
        model.set('extCmpId', record.get('extCmpId'));
        this.addField(model);
    },
    addField: function(field){
        this.fields.add(field);
    },
    getNewField: function(){
        return Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLFieldsModel');
    },
    removeJoinById: function(joinID){
        var join;
        join = this.joins.getById(joinID);
        this.joins.remove(join);
    },
    addJoin: function(join){
        this.joins.add(join);
    },
    arrayRemove: function(array, filterProperty, filterValue){
        var aReturn;
        aReturn = Ext.Array.filter(array, function(item){
            var bRemove = true;
            if (item[filterProperty] == filtervalue) {
                bRemove = false;
            }
            return bRemove;
        });
        return aReturn
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTablePanel', {
    extend: 'Ext.panel.Panel',
    alias: ['widget.sqltablepanel'],
    id: 'SQLTablePanel',
    items: [{
        xtype: 'draw',
        listeners: {
            afterrender: function(){
                this.initDropTarget();
            }
        },
        initDropTarget: function(){
            // init draw component inside qbwindow as a DropTarget
            this.dropTarget = Ext.create('Ext.dd.DropTarget', this.el, {
                ddGroup: 'sqlDDGroup',
                notifyDrop: function(source, event, data){
                    var sqlTablePanel;
                    // add a sqltable to the sqlTablePanel component
                    sqlTablePanel = Ext.getCmp('SQLTablePanel');
                    sqlTablePanel.add({
                        xtype: 'sqltable',
                        constrain: true,
                        title: data.records[0].get('text')
                    }).show();
                    return true;
                }
            });
        }
    }]
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLOutputPanel', {
    extend: 'Ext.panel.Panel',
    alias: ['widget.sqloutputpanel'],
    id: 'SQLOutputPanel',
    listeners: {
        afterlayout: function(){
            SyntaxHighlighter.highlight();
        }
    },
    initComponent: function(){
        this.callParent(arguments);
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLFieldsGrid', {
  requires: ['Ext.ux.CheckColumn'],
    extend: 'Ext.grid.Panel',
    alias: ['widget.sqlfieldsgrid'],
    id: 'SQLFieldsGrid',
    store: 'SQLFieldsStore',
    columnLines: true,
    plugins: [Ext.create('Ext.grid.plugin.CellEditing', {
        clicksToEdit: 1
    })],
    viewConfig: {
        listeners: {
            render: function(view){
                this.dd = {};
                this.dd.dropZone = new Ext.grid.ViewDropZone({
                    view: view,
                    ddGroup: 'SQLTableGridDDGroup',
                    handleNodeDrop: function(data, record, position){
                        // Was soll nach dem Drop passieren?
                    }
                });
            },
            drop: function(node, data, dropRec, dropPosition){
                // add new rows to the SQLFieldsGrid after a drop
                for (var i = 0, l = data.records.length; i < l; i++) {
                    ux.vqbuilder.sqlSelect.addFieldRecord(data.records[i], false);
                }
            }
        }
    },
    columns: [{
        xtype: 'actioncolumn',
    menuDisabled: true,
        text: 'Action',
        width: 60,
        moveGridRow: function(grid, record, index, direction){
            var store = grid.getStore();
            if (direction < 0) {
                index--;
                if (index < 0) {
                    return;
                }
            }
            else {
                index++;
                if (index >= grid.getStore().getCount()) {
                    return;
                }
            }
            // prepare manual syncing
            store.suspendAutoSync();
            // disable firing store events
            store.suspendEvents();
            // remove record and insert record at new index
            store.remove(record);
            store.insert(index, record);
            // enable firing store events
            store.resumeEvents();
            store.resumeAutoSync();
            // manual sync the store
            store.sync();
        },
        items: [{
            icon: 'resources/images/up_arrow.gif',
            tooltip: 'Move Column Up',
            getClass: function(value, metadata, record){
                var store, index;
                store = record.store;
                index = store.indexOf(record);
                if (index == 0) {
                    return 'x-action-icon-disabled';
                }
                else {
                    return 'x-grid-center-icon';
                }
            },
            handler: function(grid, rowIndex, colIndex){
                var rec = grid.getStore().getAt(rowIndex);
                this.moveGridRow(grid, rec, rowIndex, -1);
            }
        }, {
            icon: 'resources/images/down_arrow.gif',
            getClass: function(value, metadata, record){
                var store, index;
                store = record.store;
                index = store.indexOf(record);
                if ((index + 1) == store.getCount()) {
                    return 'x-action-icon-disabled';
                }
                else {
                    return 'x-grid-center-icon';
                }
            },
            tooltip: 'Move Column Down',
            handler: function(grid, rowIndex, colIndex){
                var rec = grid.getStore().getAt(rowIndex);
                this.moveGridRow(grid, rec, rowIndex, 1);
            }
        }, {
            icon: 'resources/images/remove.gif',
            iconCls: 'x-grid-center-icon',
            tooltip: 'Delete Column',
            handler: function(grid, rowIndex, colIndex){
                var rec = grid.getStore().getAt(rowIndex), store, tableId, tableGrid, selectionModel, bDel = true;
                // rec contains column grid model, the one to remove
                // get tableId of original sqltable
                tableId = rec.get('extCmpId');
                // get the sql tables grid and its selection
                tableGrid = Ext.getCmp(tableId).down('gridpanel');
                selectionModel = tableGrid.getSelectionModel();
                Ext.Array.each(selectionModel.getSelection(), function(selection){
                    // deselect the selection wich corresponds to the column 
                    // we want to remove from the column grid
                    if (rec.get('id') == selection.get('id')) {
                        // deselect current selection
                        // deselection will lead to removal, look for method deselect at the SQLTableGrid
                        selectionModel.deselect(selection);
                        bDel = false;
                    }
                });
                if (bDel) {
                    store = grid.getStore();
                    store.remove(rec);
                }
            }
        }]
    }, {
        xtype: 'checkcolumn',
    sortable: false,
        text: 'Output',
        flex: 0.075,
        menuDisabled: true,
        dataIndex: 'output',
    align: 'center'
    }, {
        xtype: 'gridcolumn',
        text: 'Expression',
    sortable: false,
    menuDisabled: true,
        flex: 0.225,
        dataIndex: 'expression',
        editor: 'textfield'
    }, {
        xtype: 'gridcolumn',
        text: 'Aggregate',
        flex: 0.125,
    sortable: false,
        menuDisabled: true,
        dataIndex: 'aggregate',
        editor: 'textfield'
    }, {
        xtype: 'gridcolumn',
        text: 'Alias',
        flex: 0.125,
    sortable: false,
        menuDisabled: true,
        dataIndex: 'alias',
        editor: 'textfield'
    }, {
        xtype: 'gridcolumn',
        text: 'Sort Type',
        flex: 0.125,
    sortable: false,
        menuDisabled: true,
        dataIndex: 'sorttype'
    }, {
        xtype: 'gridcolumn',
        text: 'Sort Order',
        flex: 0.125,
    sortable: false,
        menuDisabled: true,
        dataIndex: 'sortorder'
    }, {
        xtype: 'checkcolumn',
        text: 'Grouping',
        flex: 0.075,
    sortable: false,
        menuDisabled: true,
        dataIndex: 'grouping',
    align: 'center'
    }, {
        xtype: 'gridcolumn',
        text: 'Criteria',
        flex: 0.125,
    sortable: false,
        menuDisabled: true,
        dataIndex: 'criteria',
        editor: 'textfield'
    }],
    initComponent: function(){
        this.callParent(arguments);
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTableTree', {
    extend: 'Ext.tree.Panel',
    alias: ['widget.sqltabletree'],
    id: 'SQLTableTree',
    listeners: {
        afterrender: function(){
            this.initTreeDragZone();
        },
        itemdblclick: function(view, record, el, index, event){
            var sqlTablePanel;
            // add a sqltable to the sqlTablePanel component
            sqlTablePanel = Ext.getCmp('SQLTablePanel');
            sqlTablePanel.add({
                xtype: 'sqltable',
                constrain: true,
                title: record.get('text')
            }).show();
            
        }
    },
    initTreeDragZone: function(){
        // init tree view as a ViewDragZone
        this.view.dragZone = new Ext.tree.ViewDragZone({
            view: this.view,
            ddGroup: 'sqlDDGroup',
            dragText: '{0} ausgew盲hlte Tabelle{1}',
            repairHighlightColor: 'c3daf9',
            repairHighlight: Ext.enableFx
        });
    },
    initComponent: function(){
    
        this.store = Ext.create('Ext.data.TreeStore', {
            root: {
                text: 'Tables',
                expanded: true
            },
            proxy: {
                type: 'ajax',
                url: 'data/database.cfc?method=getTables'
            }
        });
        
        this.callParent(arguments);
    }
});
 
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTableGrid', {
    extend: 'Ext.grid.Panel',
    alias: ['widget.sqltablegrid'],
    border: false,
    hideHeaders: true,
    viewConfig: {
        listeners: {
            bodyscroll: function(){
                var scrollOffset, sqlTable;
                // the bodyscroll event of the view was fired
                // get scroll information
                scrollOffset = this.el.getScroll();
                // get the parent sqltable
                sqlTable = this.up('sqltable');
                // change shadowSprites scrollTop property
                sqlTable.shadowSprite.scrollTop = scrollOffset.top;
                // redraw all connections to reflect scroll action
                for (var i = ux.vqbuilder.connections.length; i--;) {
                    sqlTable.connection(ux.vqbuilder.connections[i]);
                }
            },
            render: function(view){
                this.dd = {};
                // init the view as a DragZone
                this.dd.dragZone = new Ext.view.DragZone({
                    view: view,
                    ddGroup: 'SQLTableGridDDGroup',
                    dragText: '{0} selected table column{1}',
                    onInitDrag: function(x, y){
                        var me = this, data = me.dragData, view = data.view, selectionModel = view.getSelectionModel(), record = view.getRecord(data.item), e = data.event;
                        data.records = [record];
                        me.ddel.update(me.getDragText());
                        me.proxy.update(me.ddel.dom);
                        me.onStartDrag(x, y);
                        return true;
                    }
                });
                // init the view as a DropZone
                this.dd.dropZone = new Ext.grid.ViewDropZone({
                    view: view,
                    ddGroup: 'SQLTableGridDDGroup',
                    handleNodeDrop: function(data, record, position){
                        // Was soll nach dem Drop passieren?
                    },
                    onNodeOver: function(node, dragZone, e, data){
                        var me = this, view = me.view, pos = me.getPosition(e, node), overRecord = view.getRecord(node), draggingRecords = data.records;
                        
                        if (!Ext.Array.contains(data.records, me.view.getRecord(node))) {
                            if (!Ext.Array.contains(draggingRecords, overRecord) && data.records[0].get('field') != '*') {
                                me.valid = true;
                                // valid drop target
                                // todo show drop invitation
                            }
                            else {
                                // invalid drop target
                                me.valid = false;
                            }
                        }
                        return me.valid ? me.dropAllowed : me.dropNotAllowed;
                    },
                    onContainerOver: function(dd, e, data){
                        var me = this;
                        // invalid drop target
                        me.valid = false;
                        return me.dropNotAllowed;
                    }
                });
            },
            drop: function(node, data, dropRec, dropPosition){
                var sqlTable1, sqlTable2, showJoinCM, connection, aBBPos, join, joinCondition = '', dropTable, targetTable;
                
                showJoinCM = function(event, el){
                    var cm;
                    // stop the browsers event bubbling
                    event.stopEvent();
                    // create context menu
                    cm = Ext.create('Ext.menu.Menu', {
                        items: [{
                            text: 'Edit Join',
                            icon: 'resources/images/document_edit16x16.gif',
                            handler: Ext.Function.bind(function(){
                            
                            }, this)
                        }, {
                            text: 'Remove Join',
                            icon: 'resources/images/remove.gif',
                            handler: Ext.Function.bind(function(){
                                // remove any connection lines from surface and from array ux.vqbuilder.connections
                                ux.vqbuilder.connections = Ext.Array.filter(ux.vqbuilder.connections, function(connection){
                                    var bRemove = true;
                                    if (this.uuid == connection.uuid) {
                                        this.line.remove();
                                        this.bgLine.remove();
                                        this.miniLine1.remove();
                                        this.miniLine2.remove();
                                        bRemove = false;
                                    }
                                    return bRemove;
                                }, this);
                                ux.vqbuilder.sqlSelect.removeJoinById(this.uuid);
                            }, this)
                        }, {
                            text: 'Close Menu',
                            icon: 'resources/images/cross.gif',
                            handler: Ext.emptyFn
                        }]
                    });
                    // show the contextmenu next to current mouse position
                    cm.showAt(event.getXY());
                };
                
                if (node.boundView) {
                    sqlTable1 = data.view.up('window');
                    sqlTable1.shadowSprite.bConnections = true;
                    
                    sqlTable2 = Ext.getCmp(node.boundView).up('window');
                    sqlTable2.shadowSprite.bConnections = true;
                    
                    dropTable = ux.vqbuilder.sqlSelect.getTableById(sqlTable1.tableId);
                    targetTable = ux.vqbuilder.sqlSelect.getTableById(sqlTable2.tableId);
                    
                    aBBPos = [data.item.viewIndex, node.viewIndex];
                    
                    connection = sqlTable2.connection(sqlTable1.shadowSprite, sqlTable2.shadowSprite, "#000", aBBPos);
                    
                    sqlTable1.connectionUUIDs.push(connection.uuid);
                    sqlTable2.connectionUUIDs.push(connection.uuid);
                    
                    ux.vqbuilder.connections.push(connection);
                    
                    // bgLine is white(invisble) and its stroke-width is 10
                    // so it is easier to capture the dblclick event
                    connection.bgLine.el.on('contextmenu', showJoinCM, connection);
                    
                    // line is black and its stroke-width is 1
                    connection.line.el.on('contextmenu', showJoinCM, connection);
                    
                    // create an instance of the join model
                    join = Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLJoin');
                    // set join id
                    join.set('id', connection.uuid);
                    // sqlTable1 is the left table
                    join.set('leftTableId', sqlTable1.tableId);
                    // data.records[0] represents the model of the dragged node
                    join.set('leftTableField', data.records[0].get('field'));
                    // sqlTable1 is the left table
                    join.set('rightTableId', sqlTable2.tableId);
                    // node.viewIndex is the index of the target node
                    join.set('rightTableField', sqlTable2.down('grid').store.getAt(node.viewIndex).get('field'));
                    // set the defaul join type to INNER
                    join.set('joinType', 'INNER');
                    
                    if (dropTable.get('tableAlias') != '') {
                        joinCondition = joinCondition + dropTable.get('tableAlias') + '.' + join.get('leftTableField') + '=';
                    }
                    else {
                        joinCondition = joinCondition + dropTable.get('tableName') + '.' + join.get('leftTableField') + '=';
                    }
                    
                    if (targetTable.get('tableAlias') != '') {
                        joinCondition = joinCondition + targetTable.get('tableAlias') + '.' + join.get('rightTableField');
                    }
                    else {
                        joinCondition = joinCondition + targetTable.get('tableName') + '.' + join.get('rightTableField');
                    }
                    
                    join.set('joinCondition', joinCondition);
                    ux.vqbuilder.sqlSelect.addJoin(join);
                }
                
            }
        }
    },
    initComponent: function(){
    
        this.columns = [{
            xtype: 'gridcolumn',
            width: 16,
            dataIndex: 'key',
            renderer: function(val, meta, model){
                if (val == 'PRI') {
                    meta.style = 'background-image:url(resources/images/key.gif) !important;background-position:2px 3px;background-repeat:no-repeat;';
                }
                return '';
            }
        }, {
            xtype: 'gridcolumn',
            flex: 1,
            dataIndex: 'field',
            renderer: function(val, meta, model){
                if (model.get('key') == 'PRI') {
                    return '<span style="font-weight: bold;">' + val + '</span><span style="color:#aaa;">' + model.get('type') + '</span>';
                }
                return val + '<span style="color:#999;">' + model.get('type') + '</span>';
                
            }
        }];
        
        this.selModel = Ext.create('Ext.selection.CheckboxModel', {
            mode: 'SIMPLE',
            checkOnly: true,
            listeners: {
                select: function(selModel, data){
                    // add new rows to the SQLFieldsGrid after a selection change
                    ux.vqbuilder.sqlSelect.addFieldRecord(data, true);
                },
                deselect: function(selModel, data){
                    var store, model;
                    // remove row from SQLFieldsGrid after deselection
                    ux.vqbuilder.sqlSelect.removeFieldById(data.get('id'));
                }
            }
        });
        
        this.callParent(arguments);
    }
});
 
Ext.define('Ext.ux.window.visualsqlquerybuilder.SQLTable', {
    extend: 'Ext.window.Window',
    minWidth: 120,
    alias: ['widget.sqltable'],
    cascadeOnFirstShow: 20,
    height: 180,
    width: 140,
    shadowSprite: {},
    layout: {
        type: 'fit'
    },
    closable: true,
    listeners: {
        show: function(){
            this.initSQLTable();
        },
        beforeclose: function(){
            this.closeSQLTable();
        }
    },
    closeSQLTable: function(){
        // remove fields / columns from sqlFieldsStore
        ux.vqbuilder.sqlSelect.removeFieldsByTableId(this.tableId);
        
        // remove table from sqlTables store inside ux.vqbuilder.sqlSelect
        ux.vqbuilder.sqlSelect.removeTableById(this.tableId);
        
        // unregister mousedown event
        this.getHeader().el.un('mousedown', this.regStartDrag, this);
        // unregister mousemove event
        Ext.EventManager.un(document, 'mousemove', this.moveWindow, this);
        // remove sprite from surface
        Ext.getCmp('SQLTablePanel').down('draw').surface.remove(this.shadowSprite, false);
        // remove any connection lines from surface and from array ux.vqbuilder.connections
        ux.vqbuilder.connections = Ext.Array.filter(ux.vqbuilder.connections, function(connection){
            var bRemove = true;
            for (var j = 0, l = this.connectionUUIDs.length; j < l; j++) {
                if (connection.uuid == this.connectionUUIDs[j]) {
                    connection.line.remove();
                    connection.bgLine.remove();
                    connection.miniLine1.remove();
                    connection.miniLine2.remove();
                    bRemove = false;
                }
            }
            return bRemove;
        }, this);
        
    },
    initSQLTable: function(){
        var sqlTablePanel, xyParentPos, xyChildPos, childSize, sprite;
        
        // get the main sqlTablePanel
        sqlTablePanel = Ext.getCmp('SQLTablePanel');
        
        // get the main sqlTablePanel position
        xyParentPos = sqlTablePanel.el.getXY();
        
        // get position of the previously added sqltable
        xyChildPos = this.el.getXY();
        
        // get the size of the previously added sqltable
        childSize = this.el.getSize();
        
        // create a sprite of type rectangle and set its position and size 
        // to position and size of the the sqltable 
        sprite = Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLTableSprite', {
            type: 'rect',
            stroke: '#fff',
            height: childSize.height - 4,
            width: childSize.width - 4,
            x: xyChildPos[0] - xyParentPos[0] + 2,
            y: xyChildPos[1] - xyParentPos[1] + 2,
            scrollTop: 0
        });
        
        // add the sprite to the surface of the sqlTablePanel
        this.shadowSprite = sqlTablePanel.down('draw').surface.add(sprite).show(true);
        
        // handle resizeing of sqltabel
        this.resizer.on('resize', function(resizer, width, height, event){
            this.shadowSprite.setAttributes({
                width: width - 6,
                height: height - 6
            }, true);
            // also move the associated connections 
            for (var i = ux.vqbuilder.connections.length; i--;) {
                this.connection(ux.vqbuilder.connections[i]);
            }
        }, this);
        
        // register a function for the mousedown event on the previously added sqltable and bind to this scope
        this.getHeader().el.on('mousedown', this.regStartDrag, this);
        
        this.getHeader().el.on('contextmenu', this.showSQLTableCM, this);
        
        this.getHeader().el.on('dblclick', this.showTableAliasEditForm, this);
        
        this.getHeader().origValue = '';
        
        // register method this.moveWindow for the mousemove event on the document and bind to this scope
        Ext.EventManager.on(document, 'mousemove', this.moveWindow, this);
        
        // register a function for the mouseup event on the document and add the this scope
        Ext.EventManager.on(document, 'mouseup', function(){
            // save the mousedown state
            this.bMouseDown = false;
        }, this);
        
        
    },
    showSQLTableCM: function(event, el){
        var cm;
        // stop the browsers event bubbling
        event.stopEvent();
        // create context menu
        cm = Ext.create('Ext.menu.Menu', {
            items: [{
                text: 'Add/Edit Alias',
                icon: 'resources/images/document_edit16x16.gif',
                handler: Ext.Function.bind(function(){
                    this.showTableAliasEditForm();
                }, this)
            }, {
                text: 'Remove Table',
                icon: 'resources/images/delete.gif',
                handler: Ext.Function.bind(function(){
                    // remove the sqltable
                    this.close();
                }, this)
            }, {
                text: 'Close Menu',
                icon: 'resources/images/cross.gif',
                handler: Ext.emptyFn
            }]
        });
        // show the contextmenu next to current mouse position
        cm.showAt(event.getXY());
    },
    showTableAliasEditForm: function(event, el){
        var table, header, title, titleId;
        table = ux.vqbuilder.sqlSelect.getTableById(this.tableId);
        header = this.getHeader();
        titleId = '#' + header.getId() + '_hd';
        title = this.down(titleId);
        header.remove(title);
        header.insert(0, [{
            xtype: 'textfield',
            flex: 0.95,
            parentCmp: header,
            parentTableModel: table,
            initComponent: function(){
            
                this.setValue(this.parentTableModel.get('tableAlias'));
                
                this.on('render', function(field, event){
                    // set focus to the textfield Benutzerkennung
                    field.focus(true, 200);
                }, this);
                
                this.on('specialkey', function(field, event){
                    if (event.getKey() == event.ENTER) {
                        if (field.getValue() != this.parentCmp.origValue) {
                            this.parentTableModel.set('tableAlias', field.getValue());
                            this.parentCmp.origValue = field.getValue();
                        }
                        this.removeTextField();
                        this.addTitle();
                    }
                }, this);
                
                this.on('blur', function(field, event){
                    if (field.getValue() != this.parentCmp.origValue) {
                        this.parentTableModel.set('tableAlias', field.getValue());
                        this.parentCmp.origValue = field.getValue();
                    }
                    this.removeTextField();
                    this.addTitle();
                }, this);
                
                this.callParent(arguments);
            },
            removeTextField: function(){
                var next;
                next = this.next();
                this.parentCmp.remove(next);
                this.parentCmp.remove(this);
            },
            addTitle: function(){
                var titleText;
                if (this.parentTableModel.get('tableAlias') != '') {
                    titleText = this.parentTableModel.get('tableAlias') + ' ( ' + this.parentTableModel.get('tableName') + ' )';
                }
                else {
                    titleText = this.parentTableModel.get('tableName');
                }
                this.parentCmp.insert(0, {
                    xtype: 'component',
                    ariaRole: 'heading',
                    focusable: false,
                    noWrap: true,
                    flex: 1,
                    id: this.parentCmp.id + '_hd',
                    style: 'text-align:' + this.parentCmp.titleAlign,
                    cls: this.parentCmp.baseCls + '-text-container',
                    renderTpl: this.parentCmp.getTpl('headingTpl'),
                    renderData: {
                        title: titleText,
                        cls: this.parentCmp.baseCls,
                        ui: this.parentCmp.ui
                    },
                    childEls: ['textEl']
                });
            }
        }, {
            xtype: 'component',
            flex: 0.05
        }]);
    },
    regStartDrag: function(){
        // save the mousedown state
        this.bMouseDown = true;
        // start the drag of the sprite
        this.shadowSprite.startDrag(this.getId());
    },
    moveWindow: function(event, domEl, opt){
        var relPosMovement;
        // check mousedown
        if (this.bMouseDown) {
            // get relative x and y values (offset)
            relPosMovement = this.getOffset('point');
            // move the sprite to the position of the window
            this.shadowSprite.onDrag(relPosMovement);
            // check if the sprite has any connections
            if (this.shadowSprite.bConnections) {
                // also move the associated connections 
                for (var i = ux.vqbuilder.connections.length; i--;) {
                    this.connection(ux.vqbuilder.connections[i]);
                }
            }
        }
    },
    getLeftRightCoordinates: function(obj1, obj2, aBBPos){
        var bb1, bb2, p = [], dx, leftBoxConnectionPoint, rightBoxConnectionPoint, dis, columHeight = 21, headerHeight = 46, LeftRightCoordinates = {};
        
        // BoundingBox Koordinaten f眉r beide Sprites abrufen
        
        bb1 = obj1.getBBox();
        // y Wert f眉r connection Points auf der linken und rechten Seite von bb1
        bb1.pY = bb1.y + headerHeight + ((aBBPos[0] - 1) * columHeight) + (columHeight / 2) - obj1.scrollTop;
        
        bb2 = obj2.getBBox();
        // y Wert f眉r connection Points auf der linken und rechten Seite von bb2
        bb2.pY = bb2.y + headerHeight + ((aBBPos[1] - 1) * columHeight) + (columHeight / 2) - obj2.scrollTop;
        
        // code f眉r linke boundingBox
        if (bb1.pY > (bb1.y + 4) && bb1.pY < (bb1.y + bb1.height - 4)) {
            p.push({
                x: bb1.x - 1, // Punkt auf linker Seite auf H枚he der verkn眉pften Spalte
                y: bb1.pY
            });
            p.push({
                x: bb1.x + bb1.width + 1, // Punkt auf rechter Seite auf H枚he der verkn眉pften Spalte
                y: bb1.pY
            });
        }
        else {
            if (bb1.pY < (bb1.y + 4)) {
                p.push({
                    x: bb1.x - 1, // Punkt auf linker Seite max. obere Position
                    y: bb1.y + 4
                });
                p.push({
                    x: bb1.x + bb1.width + 1, // Punkt auf rechter Seite max. obere Position
                    y: bb1.y + 4
                });
            }
            else {
                p.push({
                    x: bb1.x - 1, // Punkt auf linker Seite max. untere Position
                    y: bb1.y + bb1.height - 4
                });
                p.push({
                    x: bb1.x + bb1.width + 1, // Punkt auf rechter Seite max. untere Position
                    y: bb1.y + bb1.height - 4
                });
            };
                    };
        
        //  code f眉r rechte boundingBox
        if (bb2.pY > (bb2.y + 4) && bb2.pY < (bb2.y + bb2.height - 4)) {
            p.push({
                x: bb2.x - 1, // Punkt auf linker Seite auf H枚he der verkn眉pften Spalte
                y: bb2.pY
            });
            p.push({
                x: bb2.x + bb2.width + 1, // Punkt auf rechter Seite auf H枚he der verkn眉pften Spalte
                y: bb2.pY
            });
        }
        else {
            if (bb2.pY < (bb2.y + 4)) {
                p.push({
                    x: bb2.x - 1, // Punkt auf linker Seite max. obere Position
                    y: bb2.y + 4
                });
                p.push({
                    x: bb2.x + bb2.width + 1, // Punkt auf rechter Seite max. obere Position
                    y: bb2.y + 4
                });
            }
            else {
                p.push({
                    x: bb2.x - 1, // Punkt auf linker Seite max. untere Position
                    y: bb2.y + bb2.height - 4
                });
                
                p.push({
                    x: bb2.x + bb2.width + 1, // Punkt auf rechter Seite max. untere Position
                    y: bb2.y + bb2.height - 4
                });
            }
        };
        
        // Schleife 眉ber die Punkte der ersten BoundingBox
        for (var i = 0; i < 2; i++) {
            // Schleife 眉ber die Punkte der zweiten BoundingBox
            for (var j = 2; j < 4; j++) {
                // Berechnung der Offsets zwischen den jeweils vier Punkten beider BoundingBoxes
                dx = Math.abs(p[i].x - p[j].x), dy = Math.abs(p[i].y - p[j].y);
                // bb1 links mit bb2 rechts
                if (((i == 0 && j == 3) && dx < Math.abs(p[1].x - p[2].x)) || ((i == 1 && j == 2) && dx < Math.abs(p[0].x - p[3].x))) {
                    leftBoxConnectionPoint = p[i];
                    rightBoxConnectionPoint = p[j];
                }
            }
        };
        
        return {
            leftBoxConnectionPoint: leftBoxConnectionPoint,
            rightBoxConnectionPoint: rightBoxConnectionPoint
        };
        
    },
    connection: function(obj1, obj2, line, aBBPos){
        var LeftRightCoordinates, line1, line2, miniLine1, miniLine2, path, surface, color = typeof line == "string" ? line : "#000";
        
        if (obj1.line && obj1.from && obj1.to && obj1.aBBPos) {
            line = obj1;
            obj1 = line.from;
            obj2 = line.to;
            aBBPos = line.aBBPos;
        }
        
        // set reference to the wright surface
        surface = obj1.surface;
        
        // get coordinates for the left and right box
        LeftRightCoordinates = this.getLeftRightCoordinates(obj1, obj2, aBBPos);
        
        // check if the LeftBox is still on the left side or not
        if (LeftRightCoordinates.leftBoxConnectionPoint.x - LeftRightCoordinates.rightBoxConnectionPoint.x < 0) {
            line1 = 12;
            line2 = 12;
        }
        else {
            line1 = -12;
            line2 = -12;
        }
        // define the path between the left and the right box
        path = ["M", LeftRightCoordinates.leftBoxConnectionPoint.x, LeftRightCoordinates.leftBoxConnectionPoint.y, "H", LeftRightCoordinates.leftBoxConnectionPoint.x + line1, "L", LeftRightCoordinates.rightBoxConnectionPoint.x - line2, LeftRightCoordinates.rightBoxConnectionPoint.y, "H", LeftRightCoordinates.rightBoxConnectionPoint.x].join(",");
        
        miniLine1 = ["M", LeftRightCoordinates.leftBoxConnectionPoint.x, LeftRightCoordinates.leftBoxConnectionPoint.y, "H", LeftRightCoordinates.leftBoxConnectionPoint.x + line1].join(",");
        
        miniLine2 = ["M", LeftRightCoordinates.rightBoxConnectionPoint.x - line2, LeftRightCoordinates.rightBoxConnectionPoint.y, "H", LeftRightCoordinates.rightBoxConnectionPoint.x].join(",");
        
        //check if it is a new connection or not
        if (line && line.line) {
            // old connection, only change path
            line.bgLine &&
            line.bgLine.setAttributes({
                path: path
            }, true);
            line.line.setAttributes({
                path: path
            }, true);
            line.miniLine1.setAttributes({
                path: miniLine1
            }, true);
            line.miniLine2.setAttributes({
                path: miniLine2
            }, true);
        }
        else {
            // new connction, return new connection object
            return {
                line: Ext.create('Ext.draw.Sprite', {
                    type: 'path',
                    path: path,
                    stroke: color,
                    fill: 'none',
                    'stroke-width': 1,
                    surface: surface
                }).show(true),
                miniLine1: Ext.create('Ext.draw.Sprite', {
                    type: 'path',
                    path: miniLine1,
                    stroke: color,
                    fill: 'none',
                    'stroke-width': 2,
                    surface: surface
                }).show(true),
                miniLine2: Ext.create('Ext.draw.Sprite', {
                    type: 'path',
                    path: miniLine2,
                    stroke: color,
                    fill: 'none',
                    'stroke-width': 2,
                    surface: surface
                }).show(true),
                bgLine: Ext.create('Ext.draw.Sprite', {
                    type: 'path',
                    path: path,
                    opacity: 0,
                    stroke: '#fff',
                    fill: 'none',
                    'stroke-width': 10,
                    surface: surface
                }).show(true),
                from: obj1,
                to: obj2,
                aBBPos: aBBPos,
                uuid: this.createUUID()
            };
        }
    },
    initComponent: function(){
        var store, tableModel;
        
        this.connectionUUIDs = [];
        this.bMouseDown = false;
        
        // asign a uuid to the window, this builds relationship with sqlTable
        this.tableId = this.createUUID();
        
        
        store = Ext.create('Ext.data.Store', {
            autoLoad: true,
            fields: [{
                name: 'id',
                type: 'string'
            }, {
                name: 'tableName',
                type: 'string'
            }, {
                name: 'tableId',
                type: 'string',
                defaultValue: this.tableId
            }, {
                name: 'field',
                type: 'string'
            }, {
                name: 'extCmpId',
                type: 'string',
                defaultValue: this.id
            }, {
                name: 'type',
                type: 'string'
            }, {
                name: 'null',
                type: 'string'
            }, {
                name: 'key',
                type: 'string'
            }, {
                name: 'default',
                type: 'string'
            }, {
                name: 'extra',
                type: 'string'
            }],
            proxy: {
                type: 'ajax',
                url: 'data/database.cfc?method=getTableInfo',
                extraParams: {
                    tablename: this.title
                },
                reader: {
                    type: 'json'
                }
            }
        });
        
        // add sql table to ux.vqbuilder.sqlSelect tables store
        // also asign same id as stores uuid
        tableModel = Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLTableModel', {
            id: this.tableId,
            tableName: this.title,
            tableAlias: ''
        });
        ux.vqbuilder.sqlSelect.addTable(tableModel);
        
        this.items = [{
            xtype: 'sqltablegrid',
            store: store
        }];
        
        this.callParent(arguments);
    },
    getOffset: function(constrain){
        var xy = this.dd.getXY(constrain), s = this.dd.startXY;
        // return the the difference between the current and the drag&drop start position
        return [xy[0] - s[0], xy[1] - s[1]];
    },
    createUUID: function(){
        // http://www.ietf.org/rfc/rfc4122.txt
        var s = [];
        var hexDigits = "0123456789abcdef";
        for (var i = 0; i < 36; i++) {
            s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
        }
        s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
        s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
        s[8] = s[13] = s[18] = s[23] = "-";
        
        var uuid = s.join("");
        return uuid;
    },
    beforeShow: function(){
        var aWin, prev, o;
        // cascading window positions
        if (this.cascadeOnFirstShow) {
            o = (typeof this.cascadeOnFirstShow == 'number') ? this.cascadeOnFirstShow : 20;
            // get all instances from xtype sqltable
            aWin = Ext.ComponentQuery.query('sqltable');
            // start position if there is only one table
            if (aWin.length == 1) {
                this.x = o;
                this.y = o;
            }
            else {
                // loop through all instances from xtype sqltable
                for (var i = 0, l = aWin.length; i < l; i++) {
                    if (aWin[i] == this) {
                        if (prev) {
                            this.x = prev.x + o;
                            this.y = prev.y + o;
                        }
                    }
                    if (aWin[i].isVisible()) {
                        prev = aWin[i];
                    }
                }
            }
            this.setPosition(this.x, this.y);
        }
    }
});
 
Ext.define('Ext.ux.window.VisualSQLQueryBuilder', {
    extend: 'Ext.window.Window',
    alias: ['widget.qbwindow'],
    height: 620,
    width: 1000,
    layout: {
        type: 'border'
    },
    title: 'Visual SQL Query Builder',
    items: [{
        xtype: 'sqloutputpanel',
        border: false,
        region: 'center',
        autoScroll: true,
        html: '<pre class="brush: sql">SQL Output Window</pre>',
        margin: 5,
        height: 150,
        split: true
    }, {
        xtype: 'panel',
        border: false,
        height: 400,
        margin: 5,
        layout: {
            type: 'border'
        },
        region: 'north',
        split: true,
        items: [{
            xtype: 'sqltablepanel',
            border: false,
            region: 'center',
            height: 280,
            split: true,
            layout: 'fit'
        }, {
            xtype: 'sqlfieldsgrid',
            border: false,
            region: 'south',
            height: 120,
            split: true
        }, {
            xtype: 'sqltabletree',
            border: false,
            region: 'west',
            width: 200,
            height: 400,
            split: true
        }]
    }],
    initComponent: function(){
    
        // create user extension namespace ux.vqbuilder
        Ext.namespace('ux.vqbuilder');
        
        // disable gutter (linenumbers) and toolbar for SyntaxHighlighter
        SyntaxHighlighter.defaults['gutter'] = false;
        SyntaxHighlighter.defaults['toolbar'] = false;
        
        ux.vqbuilder.connections = [];
        
        ux.vqbuilder.sqlSelect = Ext.create('Ext.ux.window.visualsqlquerybuilder.SQLSelect');
        
        // add toolbar to the dockedItems
        this.dockedItems = [{
            xtype: 'toolbar',
            dock: 'top',
            items: [{
                xtype: 'tbfill'
            }, {
                text: "Save",
                icon: "resources/images/icon-save.gif"
            }, {
                text: "Run",
                icon: "resources/images/run.png"
            }]
        }];
        
        this.callParent(arguments);
    }
});

Extjs4实现的Ext.ux.window.VisualSQLQueryBuilder.rar

 

SQL在线查询设计器,强大的SQL在线查询工具 1.jpg

目录
相关文章
|
23天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
15天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
20天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2574 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
18天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
3天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
2天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
159 2
|
19天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1575 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
22天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
956 14
|
3天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
198 2
|
17天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
726 10