diff --git a/app/adapters/application.js b/app/adapters/application.js index 8eb0625..67eee3f 100644 --- a/app/adapters/application.js +++ b/app/adapters/application.js @@ -1,8 +1,9 @@ import RESTAdapter from 'ember-data/adapters/rest'; import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin'; +import ENV from '../config/environment'; export default RESTAdapter.extend(DataAdapterMixin, { - host: 'http://192.168.99.100:3000', + host: 'http://' + ENV.APP.API_HOST, namespace: 'api/v1', authorizer: 'authorizer:custom', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } diff --git a/app/authenticators/custom.js b/app/authenticators/custom.js index 54c6edf..e05c9d1 100644 --- a/app/authenticators/custom.js +++ b/app/authenticators/custom.js @@ -1,8 +1,9 @@ import Ember from 'ember'; import Base from 'ember-simple-auth/authenticators/base'; +import ENV from '../config/environment'; export default Base.extend({ - tokenEndpoint: 'http://192.168.99.100:3000/api/v1/authenticate', + tokenEndpoint: 'http://' + ENV.APP.API_HOST + '/api/v1/authenticate', restore(data) { return new Ember.RSVP.Promise(function(resolve, reject) { diff --git a/app/components/draggable-dropzone.js b/app/components/draggable-dropzone.js index aa5c2f1..3eacd71 100644 --- a/app/components/draggable-dropzone.js +++ b/app/components/draggable-dropzone.js @@ -3,7 +3,8 @@ import Ember from 'ember'; var { set } = Ember; export default Ember.Component.extend({ - classNames: [ 'draggableDropzone' ], + tagName: 'div', + classNames: [ 'draggableDropzone plots' ], classNameBindings: [ 'dragClass' ], dragClass: 'deactivated', diff --git a/app/components/draggable-item.js b/app/components/draggable-item.js index 841bd98..b02d4ac 100644 --- a/app/components/draggable-item.js +++ b/app/components/draggable-item.js @@ -1,4 +1,5 @@ import Ember from 'ember'; +import Draggable from '../mixins/draggable'; var { get } = Ember; diff --git a/app/components/plot-container.js b/app/components/plot-container.js index 2297b13..a9c99bb 100644 --- a/app/components/plot-container.js +++ b/app/components/plot-container.js @@ -1,19 +1,18 @@ import Ember from 'ember'; export default Ember.Component.extend({ - tagName: 'div', - attributeBindings: [ 'style' ], - classNames: [ 'plotContainer' ], - - plot: null, - editing: false, - - style: function() { - return 'width: ' + this.get('plot.width') + 'px; height: ' + this.get('plot.height') + 'px;'; - }.property('plot'), - isTable: function() { var type = this.get('plot.type'); return type === 'table'; + }.property('plot.type'), + + isChart: function() { + var type = this.get('plot.type'); + return type === 'chart'; + }.property('plot.type'), + + isValue: function() { + var type = this.get('plot.type'); + return type === 'value'; }.property('plot.type') }); diff --git a/app/components/plot-table.js b/app/components/plot-table.js index 7c66b4e..d8a74a2 100644 --- a/app/components/plot-table.js +++ b/app/components/plot-table.js @@ -1,8 +1,20 @@ import Ember from 'ember'; +import Resizable from '../mixins/resizable'; -export default Ember.Component.extend({ +export default Ember.Component.extend(Resizable, { tagName: 'div', - classNames: [ '' ], + attributeBindings: [ 'style' ], + classNames: [ 'plotContainer', 'plotTable' ], - editing: false + plot: null, + editing: false, + + style: function() { + return 'width: ' + this.get('plot.width') + 'px; height: ' + this.get('plot.height') + 'px;'; + }.property('plot'), + + stop_resize(event, ui) { + this.set('plot.width', this.$().width()); + this.set('plot.height', this.$().height()); + } }); diff --git a/app/components/plot-value.js b/app/components/plot-value.js index 926b613..bb27fb4 100644 --- a/app/components/plot-value.js +++ b/app/components/plot-value.js @@ -1,4 +1,23 @@ import Ember from 'ember'; +import Resizable from '../mixins/resizable'; -export default Ember.Component.extend({ +export default Ember.Component.extend(Resizable, { + tagName: 'div', + attributeBindings: [ 'style' ], + classNames: [ 'plotContainer', 'plotValue' ], + + plot: null, + editing: false, + + minWidth_resize: 50, + minHeight_resize: 20, + + style: function() { + return 'width: ' + this.get('plot.width') + 'px; height: ' + this.get('plot.height') + 'px;'; + }.property('plot'), + + stop_resize(event, ui) { + this.set('plot.width', this.$().width()); + this.set('plot.height', this.$().height()); + } }); diff --git a/app/controllers/visualization/edit.js b/app/controllers/visualization/edit.js index 9dd113f..7ca0e6f 100644 --- a/app/controllers/visualization/edit.js +++ b/app/controllers/visualization/edit.js @@ -9,7 +9,7 @@ export default Ember.Controller.extend({ // create new chart plot plot = this.store.createRecord('plot', { name: 'Chart 1', signal: 'Signal 1', type: 'chart' }); } else if (name === 'table') { - plot = this.store.createRecord('plot', { name: 'Table 1', signal: 'Signal 1', type: 'table', width: 500 }); + plot = this.store.createRecord('plot', { name: 'Table 1', signal: 'Signal 1', type: 'table', width: 500, height: 200 }); } else if (name === 'value') { plot = this.store.createRecord('plot', { name: 'Value 1', signal: 'Signal 1', type: 'value' }); } else { diff --git a/app/mixins/draggable.js b/app/mixins/draggable.js new file mode 100644 index 0000000..0fb10eb --- /dev/null +++ b/app/mixins/draggable.js @@ -0,0 +1,73 @@ +import Ember from 'ember'; + +export default Ember.Mixin.create({ + uiDragOptions: [ 'disabled_drag', 'addClasses_drag', 'appendTo_drag', 'axis_drag', + 'cancel_drag', 'connectToSortable_drag', 'containment_drag', 'cursor_drag', + 'delay_drag', 'distance_drag', 'grid_drag', 'handle_drag','helper_drag', + 'iframeFix_drag','opacity_drag','scope_drag', 'snap_drag', 'snapMode_drag', 'stack_drag' ], + uiDragEvents: [ 'create_drag', 'start_drag', 'drag_drag', 'stop_drag' ], + + didInsertElement() { + this._super(); + + // get available options and events + var options = this._gatherDragOptions(); + this._gatherDragEvents(options); + + // create a new jQuery UI widget + var ui = Ember.$.ui['draggable'](options, this.get('element')); + this.set('ui', ui); + }, + + willDestroyElement() { + var ui = this.get('ui'); + + if (ui) { + // remove all observers for jQuery UI widget + var observers = this._observers; + for (var property in observers) { + this.removeObserver(property, observers[property]); + } + + ui.destroy(); + } + }, + + _gatherDragOptions() { + // parse all options and add observers for them + var uiDragOptions = this.get('uiDragOptions') || []; + var options = {}; + + uiDragOptions.forEach(function(key) { + // save the drag option without the prefix + options[key.split('_')[0]] = this.get(key); + + // create an observer for this option + var observer = function() { + var value = this.get(key); + this.get('ui').option(key.split('_')[0], value); + }; + + this.addObserver(key, observer); + + // save observer to remove it later on + this._observers = this._observers || {}; + this._observers[key] = observer; + }, this); + + return options; + }, + + _gatherDragEvents(options) { + // register callbacks for each event + var uiDragEvents = this.get('uiDragEvents') || []; + uiDragEvents.forEach(function(event) { + var callback = this[event]; + if (callback) { + options[event.split('_')[0]] = function(event, ui) { + callback.call(this, event, ui); + }; + } + }, this); + } +}); diff --git a/app/mixins/droppable.js b/app/mixins/droppable.js new file mode 100644 index 0000000..e3c2b7f --- /dev/null +++ b/app/mixins/droppable.js @@ -0,0 +1,72 @@ +import Ember from 'ember'; + +export default Ember.Mixin.create({ + uiDropOptions: [ 'accept_drop', 'addClasses_drop', 'disabled_drop', 'greedy_drop', + 'hoverClass_drop', 'scope_drop' ], + uiDropEvents: [ 'create_drop', 'activate_drop', 'deactivate_drop', 'over_drop', + 'out_drop', 'drop_drop' ], + + didInsertElement() { + this._super(); + + // get available options and events + var options = this._gatherDropOptions(); + this._gatherDropEvents(options); + + // create a new jQuery UI widget + var ui = Ember.$.ui['droppable'](options, this.get('element')); + this.set('ui', ui); + }, + + willDestroyElement() { + var ui = this.get('ui'); + + if (ui) { + // remove all observers for jQuery UI widget + var observers = this._observers; + for (var property in observers) { + this.removeObserver(property, observers[property]); + } + + ui.destroy(); + } + }, + + _gatherDropOptions() { + // parse all options and add observers for them + var uiDropOptions = this.get('uiDropOptions') || []; + var options = {}; + + uiDropOptions.forEach(function(key) { + // save the drop option without the postfix + options[key.split('_')[0]] = this.get(key); + + // create an observer for this option + var observer = function() { + var value = this.get(key); + this.get('ui').option(key.split('_')[0], value); + }; + + this.addObserver(key, observer); + + // save observer to remove it later on + this._observers = this._observers || {}; + this._observers[key] = observer; + }, this); + + return options; + }, + + _gatherDropEvents(options) { + // register callbacks for each event + var uiDropEvents = this.get('uiDropEvents') || []; + uiDropEvents.forEach(function(event) { + var callback = this[event]; + if (callback) { + options[event.split('_')[0]] = function(event, ui) { + callback.call(this, event, ui); + }; + } + }, this); + } +}); diff --git a/app/mixins/resizable.js b/app/mixins/resizable.js new file mode 100644 index 0000000..83fa97e --- /dev/null +++ b/app/mixins/resizable.js @@ -0,0 +1,71 @@ +import Ember from 'ember'; + +export default Ember.Mixin.create({ + uiResizeOptions: [ 'disable_resize', 'alsoResize_resize', 'animate_resize', + 'animateDuration_resize', 'animateEasing_resize', 'aspectRatio_resize', + 'autoHide_resize', 'cancel_resize', 'containment_resize', 'delay_resize', + 'distance_resize', 'ghost_resize', 'grid_resize', 'handles_resize', 'helper_resize', + 'maxHeight_resize', 'maxWidth_resize', 'minHeight_resize', 'minWidth_resize' ], + uiResizeEvents: [ 'create_resize', 'start_resize', 'resize_resize', 'stop_resize' ], + + didInsertElement() { + this._super(); + + var options = this._gatherResizeOptions(); + + this._gatherResizeEvents(options); + + var ui = Ember.$.ui['resizable'](options, this.get('element')); + + this.set('ui', ui); + }, + + willDestroyElement() { + var ui = this.get('ui'); + + if (ui) { + var observers = this._observers; + for (var prop in observers) { + if (observers.hasOwnProperty(prop)) { + this.removeObserver(prop, observers[prop]); + } + } + + ui._destroy(); + } + }, + + _gatherResizeOptions() { + var uiResizeOptions = this.get('uiResizeOptions'), options = {}; + + uiResizeOptions.forEach(function(key) { + options[key.split('_')[0]] = this.get(key); + + var observer = function() { + var value = this.get(key); + console.log(key + ': ' + value); + this.get('ui').option(key.split('_')[0], value); + }; + + this.addObserver(key, observer); + + this._observers = this._observers || {}; + this._observers[key] = observer; + }, this); + + return options; + }, + + _gatherResizeEvents(options) { + var uiResizeEvents = this.get('uiResizeEvents') || [], self = this; + uiResizeEvents.forEach(function(event) { + var callback = self[event]; + + if (callback) { + options[event.split('_')[0]] = function(event, ui) { + callback.call(self, event, ui); + }; + } + }); + } +}); diff --git a/app/models/plot.js b/app/models/plot.js index ec1bb8f..2e88f8a 100644 --- a/app/models/plot.js +++ b/app/models/plot.js @@ -1,6 +1,6 @@ import Model from 'ember-data/model'; import attr from 'ember-data/attr'; -// import { belongsTo, hasMany } from 'ember-data/relationships'; +import { belongsTo } from 'ember-data/relationships'; export default Model.extend({ name: attr('string'), @@ -8,5 +8,8 @@ export default Model.extend({ width: attr('number', { defaultValue: 100 }), height: attr('number', { defaultValue: 100 }), title: attr('string'), - type: attr('string') + type: attr('string'), + row: attr('number'), + column: attr('number'), + visualization: belongsTo('Visualization', { async: true }) }); diff --git a/app/models/visualization.js b/app/models/visualization.js index 3779d59..016fb6e 100644 --- a/app/models/visualization.js +++ b/app/models/visualization.js @@ -5,5 +5,6 @@ import { belongsTo, hasMany } from 'ember-data/relationships'; export default Model.extend({ name: attr('string'), plots: hasMany('plot', { async: true }), - project: belongsTo('project', { async: true }) + project: belongsTo('project', { async: true }), + rows: attr('number', { defaultValue: 1 }) }); diff --git a/app/styles/app.css b/app/styles/app.css index 83d5df6..9487c40 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -43,6 +43,8 @@ footer { margin-top: 20px; text-align: center; + + clear: both; } /** @@ -111,16 +113,28 @@ footer { .plots { margin-top: 20px; margin-bottom: 20px; + + overflow: auto; } .plot-toolbox { margin-top: 20px; } +.plot-add-row { + font-size: 100px; + font-weight: 800; + + text-align: center; + + margin: 0; + margin-top: -25px; +} + .draggableDropzone { display: block; - min-height: 200px; + min-height: 100px; border: 3px dashed #aaa; @@ -151,6 +165,12 @@ footer { margin: 10px; padding: 5px 10px; + + float: left; +} + +.plotValue { + } .plotTable { diff --git a/app/templates/components/plot-container.hbs b/app/templates/components/plot-container.hbs index b223224..b8a5f06 100644 --- a/app/templates/components/plot-container.hbs +++ b/app/templates/components/plot-container.hbs @@ -1,5 +1,9 @@ {{#if isTable}} {{#plot-table plot=plot editing=editing}}{{/plot-table}} +{{else if isChart}} + {{#plot-chart plot=plot editing=editing}}{{/plot-chart}} +{{else if isValue}} + {{#plot-value plot=plot editing=editing}}{{/plot-value}} {{else}} - Plot + Unknown Plot {{/if}} diff --git a/app/templates/components/plot-value.hbs b/app/templates/components/plot-value.hbs index 889d9ee..af53c9c 100644 --- a/app/templates/components/plot-value.hbs +++ b/app/templates/components/plot-value.hbs @@ -1 +1 @@ -{{yield}} +Value diff --git a/app/templates/visualization/edit.hbs b/app/templates/visualization/edit.hbs index d23a362..2ced225 100644 --- a/app/templates/visualization/edit.hbs +++ b/app/templates/visualization/edit.hbs @@ -15,13 +15,15 @@ {{/draggable-item}} -
- {{#draggable-dropzone dropped='addPlot'}} - {{#each model.plots as |plot|}} - {{#plot-container plot=plot editing=true}}{{/plot-container}} - {{/each}} - {{/draggable-dropzone}} -
+{{#draggable-dropzone dropped='addPlot'}} + {{#each model.plots as |plot|}} + {{#plot-container plot=plot editing=true}}{{/plot-container}} + {{/each}} +{{/draggable-dropzone}} + +{{#draggable-dropzone dropped='addRowWithPlot'}} +
+
+{{/draggable-dropzone}}

diff --git a/bower.json b/bower.json index edb8acd..d2c82e9 100644 --- a/bower.json +++ b/bower.json @@ -4,6 +4,7 @@ "ember": "~2.5.0", "ember-cli-shims": "0.1.1", "ember-cli-test-loader": "0.2.2", - "ember-qunit-notifications": "0.1.0" + "ember-qunit-notifications": "0.1.0", + "jquery-ui": "1.11.4" } } diff --git a/config/environment.js b/config/environment.js index ea199a4..f359e20 100644 --- a/config/environment.js +++ b/config/environment.js @@ -14,8 +14,7 @@ module.exports = function(environment) { }, APP: { - // Here you can pass flags/options to your application instance - // when it is created + API_HOST: 'localhost:3000' } }; diff --git a/package.json b/package.json index 5e45d0c..39de3e5 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "ember-cli-htmlbars": "^1.0.3", "ember-cli-htmlbars-inline-precompile": "^0.3.1", "ember-cli-inject-live-reload": "^1.4.0", + "ember-cli-jquery-ui": "0.0.20", "ember-cli-jshint": "^1.0.0", "ember-cli-qunit": "^1.4.0", "ember-cli-release": "0.2.8", diff --git a/tests/unit/mixins/draggable-test.js b/tests/unit/mixins/draggable-test.js new file mode 100644 index 0000000..e13c254 --- /dev/null +++ b/tests/unit/mixins/draggable-test.js @@ -0,0 +1,12 @@ +import Ember from 'ember'; +import DraggableMixin from 'villasweb-frontend/mixins/draggable'; +import { module, test } from 'qunit'; + +module('Unit | Mixin | draggable'); + +// Replace this with your real tests. +test('it works', function(assert) { + let DraggableObject = Ember.Object.extend(DraggableMixin); + let subject = DraggableObject.create(); + assert.ok(subject); +}); diff --git a/tests/unit/mixins/droppable-test.js b/tests/unit/mixins/droppable-test.js new file mode 100644 index 0000000..41976f3 --- /dev/null +++ b/tests/unit/mixins/droppable-test.js @@ -0,0 +1,12 @@ +import Ember from 'ember'; +import DroppableMixin from 'villasweb-frontend/mixins/droppable'; +import { module, test } from 'qunit'; + +module('Unit | Mixin | droppable'); + +// Replace this with your real tests. +test('it works', function(assert) { + let DroppableObject = Ember.Object.extend(DroppableMixin); + let subject = DroppableObject.create(); + assert.ok(subject); +}); diff --git a/tests/unit/mixins/resizable-test.js b/tests/unit/mixins/resizable-test.js new file mode 100644 index 0000000..eaec854 --- /dev/null +++ b/tests/unit/mixins/resizable-test.js @@ -0,0 +1,12 @@ +import Ember from 'ember'; +import ResizableMixin from 'villasweb-frontend/mixins/resizable'; +import { module, test } from 'qunit'; + +module('Unit | Mixin | resizable'); + +// Replace this with your real tests. +test('it works', function(assert) { + let ResizableObject = Ember.Object.extend(ResizableMixin); + let subject = ResizableObject.create(); + assert.ok(subject); +}); diff --git a/todo.md b/todo.md index 3f09a5b..4b9a455 100644 --- a/todo.md +++ b/todo.md @@ -1,3 +1,12 @@ # To-Do - Change password - Don't log out on unauthorized access (admin level lower than required) + - Move plot attributes/styling from plot-container into actual plots + - Move drag-n-drop to mixins + - Websocket node is working in develop branch + - Add API host to config/environment.js + - Empty visualization after delete + + +websocketserverip/config.json +websocketserverip/nodes.json