Browse Source

Initial commit

pull/2/head
artur root 10 years ago
commit
69cfec68bd
  1. 7
      .gitignore
  2. 137
      Gruntfile.js
  3. 27
      LICENSE.md
  4. 29
      README.md
  5. 52
      bundle/asset_manager/config/config.js
  6. 70
      bundle/asset_manager/main.js
  7. 36
      bundle/asset_manager/model/Asset.js
  8. 18
      bundle/asset_manager/model/AssetImage.js
  9. 11
      bundle/asset_manager/model/Assets.js
  10. 7
      bundle/asset_manager/template/assetImage.html
  11. 5
      bundle/asset_manager/template/fileUploader.html
  12. 89
      bundle/asset_manager/view/AssetImageView.js
  13. 17
      bundle/asset_manager/view/AssetView.js
  14. 122
      bundle/asset_manager/view/AssetsView.js
  15. 102
      bundle/asset_manager/view/FileUploader.js
  16. 10
      bundle/canvas/config/config.js
  17. 60
      bundle/canvas/main.js
  18. 14
      bundle/canvas/model/Canvas.js
  19. 25
      bundle/canvas/view/CanvasView.js
  20. 6
      bundle/code_manager/config/config.js
  21. 254
      bundle/code_manager/main.js
  22. 57
      bundle/code_manager/model/CodeMirrorEditor.js
  23. 44
      bundle/code_manager/model/CssGenerator.js
  24. 31
      bundle/code_manager/model/EditorInterface.js
  25. 26
      bundle/code_manager/model/GeneratorInterface.js
  26. 39
      bundle/code_manager/model/HtmlGenerator.js
  27. 40
      bundle/code_manager/model/JsonGenerator.js
  28. 4
      bundle/code_manager/template/editor.html
  29. 25
      bundle/code_manager/view/EditorView.js
  30. 25
      bundle/commands/config/config.js
  31. 90
      bundle/commands/main.js
  32. 13
      bundle/commands/model/Command.js
  33. 11
      bundle/commands/model/Commands.js
  34. 51
      bundle/commands/view/CommandAbstract.js
  35. 269
      bundle/commands/view/CreateComponent.js
  36. 59
      bundle/commands/view/DeleteComponent.js
  37. 73
      bundle/commands/view/ExportTemplate.js
  38. 39
      bundle/commands/view/ImageComponent.js
  39. 79
      bundle/commands/view/InsertCustom.js
  40. 186
      bundle/commands/view/MoveComponent.js
  41. 35
      bundle/commands/view/OpenLayers.js
  42. 36
      bundle/commands/view/OpenStyleManager.js
  43. 57
      bundle/commands/view/ResizeComponent.js
  44. 165
      bundle/commands/view/SelectComponent.js
  45. 375
      bundle/commands/view/SelectPosition.js
  46. 17
      bundle/commands/view/SwitchVisibility.js
  47. 44
      bundle/commands/view/TextComponent.js
  48. 49
      bundle/config/require-config.js
  49. 60
      bundle/dev.js
  50. 20
      bundle/dom_components/config/config.js
  51. 46
      bundle/dom_components/main.js
  52. 44
      bundle/dom_components/model/Component.js
  53. 14
      bundle/dom_components/model/ComponentImage.js
  54. 14
      bundle/dom_components/model/ComponentText.js
  55. 41
      bundle/dom_components/model/Components.js
  56. 65
      bundle/dom_components/view/ComponentImageView.js
  57. 72
      bundle/dom_components/view/ComponentTextView.js
  58. 152
      bundle/dom_components/view/ComponentView.js
  59. 96
      bundle/dom_components/view/ComponentsView.js
  60. 72
      bundle/editor/config/config.js
  61. 39
      bundle/editor/main.js
  62. 336
      bundle/editor/model/Editor.js
  63. 33
      bundle/editor/view/EditorView.js
  64. 157
      bundle/main.js
  65. 13
      bundle/modal_dialog/config/config.js
  66. 57
      bundle/modal_dialog/main.js
  67. 15
      bundle/modal_dialog/model/Modal.js
  68. 11
      bundle/modal_dialog/template/modal.html
  69. 110
      bundle/modal_dialog/view/ModalView.js
  70. 10
      bundle/navigator/config/config.js
  71. 40
      bundle/navigator/main.js
  72. 15
      bundle/navigator/template/item.html
  73. 330
      bundle/navigator/view/ItemSort.js
  74. 198
      bundle/navigator/view/ItemView.js
  75. 89
      bundle/navigator/view/ItemsView.js
  76. 48
      bundle/panel/config/config.js
  77. 74
      bundle/panel/main.js
  78. 26
      bundle/panel/model/Button.js
  79. 44
      bundle/panel/model/Buttons.js
  80. 22
      bundle/panel/model/Panel.js
  81. 11
      bundle/panel/model/Panels.js
  82. 236
      bundle/panel/view/ButtonView.js
  83. 68
      bundle/panel/view/ButtonsView.js
  84. 50
      bundle/panel/view/PanelView.js
  85. 67
      bundle/panel/view/PanelsView.js
  86. 23
      bundle/rich_text_editor/config/config.js
  87. 131
      bundle/rich_text_editor/main.js
  88. 16
      bundle/rich_text_editor/model/CommandButton.js
  89. 11
      bundle/rich_text_editor/model/CommandButtons.js
  90. 20
      bundle/rich_text_editor/view/CommandButtonView.js
  91. 51
      bundle/rich_text_editor/view/CommandButtonsView.js
  92. 51
      bundle/storage_manager/config/config.js
  93. 184
      bundle/storage_manager/main.js
  94. 59
      bundle/storage_manager/model/LocalStorage.js
  95. 80
      bundle/storage_manager/model/RemoteStorage.js
  96. 19
      bundle/storage_manager/model/StorageInterface.js
  97. 431
      bundle/style_manager/config/config.js
  98. 80
      bundle/style_manager/main.js
  99. 16
      bundle/style_manager/model/Layer.js
  100. 11
      bundle/style_manager/model/Layers.js

7
.gitignore

@ -0,0 +1,7 @@
.DS_Store
.settings/
.project
private/
libs/
node_modules/

137
Gruntfile.js

@ -0,0 +1,137 @@
module.exports = function(grunt) {
var appPath = 'bundle',
buildPath = 'dist',
configPath = 'config/require-config.js';
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-mocha');
grunt.initConfig({
appDir: appPath,
builtDir: buildPath,
pkg: grunt.file.readJSON("package.json"),
requirejs:{
compile:{
options: {
mainConfigFile: '<%= appDir %>/' + configPath,
appDir: '<%= appDir %>',
dir: '<%= builtDir %>',
baseUrl: './',
name: 'main',
removeCombined: true,
findNestedDependencies: true,
keepBuildDir: true,
inlineText: true,
optimize: 'none'
//paths: { "jquery": "empty:" }, //try to exclude
}
}
},
jshint: {
all: [
'Gruntfile.js',
'<%= appDir %>/**/*.js',
]
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */'
},
build:{
files: {
'<%= builtDir %>/grapes.min.js': ['<%= builtDir %>/main.js']
}
}
},
sass: {
dist: {
files: [{
expand: true,
cwd: 'styles/scss',
src: ['**/*.scss'],
dest: 'styles/css',
ext: '.css'
}]
}
},
mocha: {
test: {
src: ['test/index.html'],
options: { log: true, },
},
},
connect: {
/*
app: {
options: {
port: 8001,
open: {
target: 'http://localhost:8001',
//appName: 'Firefox' // 'Google Chrome'
}
}
},
*/
test: {
options: {
open: {
target: 'http://localhost:8000/test',
}
}
}
},
watch: {
script: {
files: [ '<%= appDir %>/**/*.js' ],
tasks: ['jshint']
},
css: {
files: '**/*.scss',
tasks: ['sass']
},
test: {
files: [ 'test/specs/**/*.js' ],
tasks: ['mocha'],
options: { livereload: true }, //default port 35729
}
}
});
/**
* Need to copy require configs cause r.js will try to load them from the path indicated inside
* main.js file. This is the only way I have found to do it and only for the pleasure of using separate config
* requirejs file.
* */
grunt.registerTask('before-requirejs', function() {
//if(grunt.file.exists(buildPath))
//grunt.file.delete(buildPath);
grunt.file.mkdir(buildPath);
grunt.file.copy(appPath + '/' + configPath, buildPath + '/' + appPath + '/' + configPath);
});
grunt.registerTask('after-requirejs', function() {
//grunt.file.copy(buildPath + '/main.js', buildPath + '/main.min.js');
});
grunt.registerTask('dev', ['connect', 'watch']);
grunt.registerTask('test', ['mocha']);
grunt.registerTask('deploy', ['jshint', 'before-requirejs', 'requirejs', 'after-requirejs', 'uglify']);
grunt.registerTask('default', ['dev']);
};

27
LICENSE.md

@ -0,0 +1,27 @@
# Grapes.js
Copyright (c) Artur Arseniev
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name "Grapes" nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

29
README.md

@ -0,0 +1,29 @@
# README #
This README would normally document whatever steps are necessary to get your application up and running.
### What is this repository for? ###
* Quick summary
* Version
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
### How do I get set up? ###
* Summary of set up
* Configuration
* Dependencies
* Database configuration
* How to run tests
* Deployment instructions
### Contribution guidelines ###
* Writing tests
* Code review
* Other guidelines
### Who do I talk to? ###
* Repo owner or admin
* Other community or team contact

52
bundle/asset_manager/config/config.js

@ -0,0 +1,52 @@
define(function () {
return {
// Style prefix
stylePrefix : 'am-',
// Default assets
assets : [],
// Indicates which storage to use. Available: local | remote
storageType : 'local',
// The name that will be used to identify assets inside storage.
// If empty will be used: prefix + 'assets'
storageName : 'assets',
// Where store remote assets
urlStore : 'http://localhost/assets/store',
// Where fetch remote assets
urlLoad : 'http://localhost/assets/load',
// Custom parameters to pass with set request
paramsStore : {},
// Custom parameters to pass with get request
paramsLoad : {},
// Callback before request
beforeSend : function(jqXHR,settings){},
// Callback after request
onComplete : function(jqXHR,status){},
// Url where uploads will be send
urlUpload : 'http://localhost/assets/upload',
// Text on upload input
uploadText : 'Drop files here or click to upload',
// Disable upload input
disableUpload : false,
// Store assets data where the new one is added or deleted
storeOnChange : true,
// It could be useful avoid to send other requests, for saving assets,
// after each upload because the uploader script has already done it
storeAfterUpload : false,
};
});

70
bundle/asset_manager/main.js

@ -0,0 +1,70 @@
define(function(require) {
/**
* @class AssetManager
* @param {Object} Configurations
*
* @return {Object}
* */
var AssetManager = function(config)
{
var c = config || {},
defaults = require('./config/config'),
Assets = require('./model/Assets'),
AssetsView = require('./view/AssetsView'),
FileUpload = require('./view/FileUploader');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.assets = new Assets(c.assets);
var obj = {
collection : this.assets,
config : c,
};
this.am = new AssetsView(obj);
this.fu = new FileUpload(obj);
};
AssetManager.prototype = {
/**
* Get collection of assets
*
* @return {Object}
* */
getAssets : function(){
return this.assets;
},
/**
* Set new target
* @param {Object} m Model
*
* @return void
* */
setTarget : function(m){
this.am.collection.target = m;
},
/**
* Set callback after asset was selected
* @param {Object} f Callback function
*
* @return void
* */
onSelect : function(f){
this.am.collection.onSelect = f;
},
render : function(){
if(!this.rendered)
this.rendered = this.am.render().$el.add(this.fu.render().$el);
return this.rendered;
},
};
return AssetManager;
});

36
bundle/asset_manager/model/Asset.js

@ -0,0 +1,36 @@
define(['backbone'],
function (Backbone) {
/**
* @class Asset
* */
return Backbone.Model.extend({
defaults: {
type: 'none', //Type of the asset
src: '', //Location
},
initialize: function(options) {
this.options = options || {};
},
/**
* Get filename of the asset
*
* @return {String}
* */
getFilename: function(){
return this.get('src').split('/').pop();
},
/**
* Get extension of the asset
*
* @return {String}
* */
getExtension: function(){
return this.getFilename().split('.').pop();
},
});
});

18
bundle/asset_manager/model/AssetImage.js

@ -0,0 +1,18 @@
define(['backbone', './Asset'],
function (Backbone, Asset) {
/**
* @class AssetImage
* */
return Asset.extend({
defaults: _.extend({},Asset.prototype.defaults,
{
type: 'image',
unitDim: 'px',
height: 0,
width: 0,
}
),
});
});

11
bundle/asset_manager/model/Assets.js

@ -0,0 +1,11 @@
define(['backbone','./Asset'],
function (Backbone, Asset) {
/**
* @class Assets
* */
return Backbone.Collection.extend({
model: Asset,
});
});

7
bundle/asset_manager/template/assetImage.html

@ -0,0 +1,7 @@
<div id="<%= pfx %>preview" style="background-image: url(<%= src %>);"></div>
<div id="<%= pfx %>meta">
<div id="<%= pfx %>name"><%= name %></div>
<div id="<%= pfx %>dimensions"><%= dim %></div>
</div>
<div id="<%= pfx %>close">&Cross;</div>
<div style="clear:both"></div>

5
bundle/asset_manager/template/fileUploader.html

@ -0,0 +1,5 @@
<form>
<div id="<%= pfx %>title"><%= title %></div>
<input type="file" id="<%= uploadId %>" name="file" accept="image/*" <%= disabled ? 'disabled' : '' %> multiple/>
<div style="clear:both;"></div>
</form>

89
bundle/asset_manager/view/AssetImageView.js

@ -0,0 +1,89 @@
define(['./AssetView','text!./../template/assetImage.html'],
function (AssetView, assetTemplate) {
/**
* @class AssetImageView
* */
return AssetView.extend({
events:{
'click' : 'selected',
'dblclick' : 'chosen',
},
template: _.template(assetTemplate),
initialize: function(o) {
AssetView.prototype.initialize.apply(this, arguments);
this.className += ' ' + this.pfx + 'asset-image';
this.events['click #' + this.pfx + 'close'] = 'removeItem';
},
/**
* Trigger when asset is been selected
*
* @return void
* */
selected: function(){
this.model.collection.trigger('deselectAll');
this.$el.addClass(this.pfx + 'highlight');
this.updateTarget(this.model.get('src'));
},
/**
* Trigger when asset is been chosen (double clicked)
*
* @return void
* */
chosen: function(){
this.updateTarget(this.model.get('src'));
var f = this.model.collection.onSelect;
if(f && typeof f == 'function'){
f(this.model);
}
},
/**
* Update target if exists
* @param {String} v Value
*
* @return void
* */
updateTarget: function(v){
var target = this.model.collection.target;
if(target && target.set){
var attr = _.clone( target.get('attributes') );
attr['class'] = [];
target.set('attributes', attr );
target.set('src', v );
}
},
/**
* Remove asset from collection
*
* @return void
* */
removeItem: function(e){
e.stopPropagation();
this.model.collection.remove(this.model);
},
render : function(){
var name = this.model.get('name'),
dim = this.model.get('width') && this.model.get('height') ?
this.model.get('width')+' x '+this.model.get('height') : '';
name = name ? name : this.model.get('src').split("/").pop();
name = name && name.length > 30 ? name.substring(0, 30)+'...' : name;
dim = dim ? dim + (this.model.get('unitDim') ? this.model.get('unitDim') : ' px' ) : '';
this.$el.html( this.template({
name: name,
src: this.model.get('src'),
dim: dim,
pfx: this.pfx
}));
this.$el.attr('class', this.className);
return this;
},
});
});

17
bundle/asset_manager/view/AssetView.js

@ -0,0 +1,17 @@
define(['backbone'],
function (Backbone) {
/**
* @class AssetView
* */
return Backbone.View.extend({
initialize: function(o) {
this.options = o;
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
this.className = this.pfx + 'asset';
this.listenTo( this.model, 'destroy remove', this.remove );
},
});
});

122
bundle/asset_manager/view/AssetsView.js

@ -0,0 +1,122 @@
define(['backbone', './AssetView', './AssetImageView', './FileUploader'],
function (Backbone, AssetView, AssetImageView, FileUploader) {
/**
* @class AssetsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.options = o;
this.config = o.config;
this.pfx = this.config.stylePrefix;
this.listenTo( this.collection, 'add', this.addToAsset );
this.listenTo( this.collection, 'deselectAll', this.deselectAll );
this.className = this.pfx + 'assets';
// Check if storage is required and if Storage Manager is available
if(this.config.stm && this.config.storageType !== ''){
var type = this.config.storageType;
this.provider = this.config.stm.getProvider(type);
this.storeName = this.config.storageName ? this.config.storageName : this.className;
if(this.provider){
// Create new instance of provider
this.storagePrv = this.provider.clone().set(this.config);
this.collection.reset();
this.collection.add(this.load());
if(this.config.storeOnChange){
var ev = 'remove' + (this.config.storeAfterUpload ? ' add' : '');
this.listenTo(this.collection, ev, this.store);
}
}
}
},
/**
* Store collection
*
* @return void
* */
store: function(){
if(this.storagePrv)
this.storagePrv.store(this.storeName, JSON.stringify(this.collection.toJSON()) );
},
/**
* Load collection
*
* @return {Object}
* */
load: function(){
var result = null;
if(this.storagePrv)
result = this.storagePrv.load(this.storeName);
if(typeof result !== 'object'){
try{
result = JSON.parse(result);
}catch(err){
console.warn(err);
}
}
return result;
},
/**
* Add asset to collection
* */
addToAsset: function(model){
this.addAsset(model);
},
/**
* Add new asset to collection
* @param Object Model
* @param Object Fragment collection
*
* @return Object Object created
* */
addAsset: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = AssetView;
if(model.get('type').indexOf("image") > -1)
viewObject = AssetImageView;
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild( rendered );
}else{
this.$el.prepend(rendered);
}
return rendered;
},
/**
* Deselect all assets
*
* @return void
* */
deselectAll: function(){
this.$el.find('.' + this.pfx + 'highlight').removeClass(this.pfx + 'highlight');
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addAsset(model, fragment);
},this);
this.$el.append(fragment);
this.$el.attr('class', this.className);
return this;
}
});
});

102
bundle/asset_manager/view/FileUploader.js

@ -0,0 +1,102 @@
define(['backbone', 'text!./../template/fileUploader.html'],
function (Backbone, fileUploaderTemplate) {
/**
* @class FileUploader
* */
return Backbone.View.extend({
template: _.template(fileUploaderTemplate),
events: {},
initialize: function(o) {
this.options = o || {};
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
this.target = this.collection || {};
this.uploadId = this.pfx + 'uploadFile';
this.disabled = this.config.disableUpload;
this.events['change #' + this.uploadId] = 'uploadFile';
},
/**
* Upload files
* @param {Object} e Event
*
* @return void
* */
uploadFile : function(e){
var files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
formData = new FormData();
for (var i = 0; i < files.length; i++) {
formData.append('files[]', files[i]);
}
var target = this.target;
$.ajax({
url : this.config.urlUpload, //this.config.urlUpload
type : 'POST',
data : formData,
beforeSend : this.config.beforeSend,
complete : this.config.onComplete,
xhrFields : {
onprogress: function (e) {
if (e.lengthComputable) {
/*var result = e.loaded / e.total * 100 + '%';*/
}
},
onload: function (e) {
//progress.value = 100;
}
},
cache: false, contentType: false, processData: false
}).done(function(data){
target.add(data.data);
}).always(function(){
//turnOff loading
});
},
/**
* Make input file droppable
*
* @return void
* */
initDrop: function(){
var that = this;
if(!this.uploadForm){
this.uploadForm = this.$el.find('form').get(0);
if( 'draggable' in this.uploadForm ){
var uploadFile = this.uploadFile;
this.uploadForm.ondragover = function(){
this.className = that.pfx + 'hover';
return false;
};
this.uploadForm.ondragleave = function(){
this.className = '';
return false;
};
this.uploadForm.ondrop = function(e){
this.className = '';
e.preventDefault();
that.uploadFile(e);
return;
};
}
}
},
render : function(){
this.$el.html( this.template({
title : this.config.uploadText,
uploadId : this.uploadId,
disabled : this.disabled,
pfx : this.pfx
}) );
this.initDrop();
this.$el.attr('class', this.pfx + 'file-uploader');
return this;
},
});
});

10
bundle/canvas/config/config.js

@ -0,0 +1,10 @@
define(function () {
return {
stylePrefix : 'cv-',
// Coming soon
rulers : false,
};
});

60
bundle/canvas/main.js

@ -0,0 +1,60 @@
define(function(require) {
/**
* @class Canvas
* @param {Object} Configurations
*
* @return {Object}
* */
var Canvas = function(config)
{
var c = config || {},
defaults = require('./config/config'),
Canvas = require('./model/Canvas'),
CanvasView = require('./view/CanvasView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.canvas = new Canvas(config);
var obj = {
model : this.canvas,
config : c,
};
this.CanvasView = new CanvasView(obj);
};
Canvas.prototype = {
/**
* Add wrapper
* @param {Object} wrp Wrapper
*
* */
setWrapper : function(wrp)
{
this.canvas.set('wrapper', wrp);
},
/**
* Get wrapper
*
* @return {Object}
* */
getWrapper : function()
{
return this.canvas.get('wrapper').getComponent();
},
/**
* Render canvas
* */
render : function()
{
return this.CanvasView.render().$el;
},
};
return Canvas;
});

14
bundle/canvas/model/Canvas.js

@ -0,0 +1,14 @@
define(['backbone'],
function(Backbone){
/**
* @class Canvas
* */
return Backbone.Model.extend({
defaults :{
wrapper : '',
rulers : false,
},
});
});

25
bundle/canvas/view/CanvasView.js

@ -0,0 +1,25 @@
define(['backbone'],
function(Backbone) {
/**
* @class CanvasView
* */
return Backbone.View.extend({
id: 'canvas',
initialize: function(o) {
this.config = o.config;
this.className = this.config.stylePrefix + 'canvas';
},
render: function() {
this.wrapper = this.model.get('wrapper');
if(this.wrapper && typeof this.wrapper.render == 'function'){
this.$el.append( this.wrapper.render() );
}
this.$el.attr('class', this.className);
return this;
},
});
});

6
bundle/code_manager/config/config.js

@ -0,0 +1,6 @@
define(function () {
return {
// Style prefix
stylePrefix : 'cm-',
};
});

254
bundle/code_manager/main.js

@ -0,0 +1,254 @@
define(function(require) {
/**
* @class CodeManager
* @param {Object} Configurations
*
* @return {Object}
* */
function CodeManager(config)
{
var c = config || {},
defaults = require('./config/config'),
gInterface = require('./model/GeneratorInterface'),
gHtml = require('./model/HtmlGenerator'),
gCss = require('./model/CssGenerator'),
gJson = require('./model/JsonGenerator'),
eInterface = require('./model/EditorInterface'),
eCM = require('./model/CodeMirrorEditor'),
editorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.gi = new gInterface();
this.generators = {};
this.defaultGenerators = {};
this.currentGenerator = null;
this.ei = new eInterface();
this.editors = {};
this.defaultEditors = {};
this.currentEditor = null;
var geHtml = new gHtml(),
geCss = new gCss(),
geJson = new gJson(),
edCM = new eCM();
this.defaultGenerators[geHtml.getId()] = geHtml;
this.defaultGenerators[geCss.getId()] = geCss;
this.defaultGenerators[geJson.getId()] = geJson;
this.defaultEditors[edCM.getId()] = edCM;
this.EditorView = editorView;
this.config = c;
}
CodeManager.prototype = {
/**
* Add new code generator
* @param {GeneratorInterface} generator
*
* @return this
* */
addGenerator : function(generator)
{
// Check interface implementation
for (var method in this.gi)
if(!generator[method]){
console.warn("addGenerator: method '"+ method +"' was not found");
return;
}
var id = generator.getId();
this.generators[id] = generator;
if(!this.currentGenerator)
this.currentGenerator = id;
return this;
},
/**
* Returns generator
* @param {String}|{Integer} id Generator ID
*
* @return {GeneratorInterface}|null
* */
getGenerator : function(id)
{
if(id && this.generators[id])
generator = this.generators[id];
return generator ? generator : null;
},
/**
* Returns generators
*
* @return {Array}
* */
getGenerators : function()
{
return this.generators;
},
/**
* Get current generator
*
* @return {GeneratorInterface}
* */
getCurrentGenerator : function()
{
if(!this.currentGenerator)
this.loadDefaultGenerators();
return this.getGenerator(this.currentGenerator);
},
/**
* Set current generator
* @param {Integer} id Generator ID
*
* @return this
* */
setCurrentGenerator : function(id)
{
this.currentGenerator = id;
return this;
},
/**
* Load default generators
*
* @return this
* */
loadDefaultGenerators : function()
{
for (var id in this.defaultGenerators) {
this.addGenerator(this.defaultGenerators[id]);
}
return this;
},
/**
* Add new editor
* @param {EditorInterface} editor
*
* @return this
* */
addEditor : function(editor)
{
// Check interface implementation
for (var method in this.ei)
if(!editor[method]){
console.warn("addEditor: method '"+ method +"' was not found");
return;
}
var id = editor.getId();
this.editors[id] = editor;
if(!this.currentEditor)
this.currentEditor = id;
return this;
},
/**
* Returns editor
* @param {String}|{Integer} id Editor ID
*
* @return {EditorInterface}|null
* */
getEditor : function(id)
{
if(id && this.editors[id])
editor = this.editors[id];
return editor ? editor : null;
},
/**
* Returns editors
*
* @return {Array}
* */
getEditors : function()
{
return this.editors;
},
/**
* Get current editor
*
* @return {EditorInterface}
* */
getCurrentEditor : function()
{
if(!this.currentEditor)
this.loadDefaultEditors();
return this.getEditor(this.currentEditor);
},
/**
* Set current editor
* @param {Integer} id Editor ID
*
* @return this
* */
setCurrentEditor : function(id)
{
this.currentEditor = id;
return this;
},
/**
* Load default editors
*
* @return this
* */
loadDefaultEditors : function()
{
for (var id in this.defaultEditors) {
this.addEditor(this.defaultEditors[id]);
}
return this;
},
/**
* Get code by name
* @param {Backbone.Model} model Model
* @param {String}|{Integer} v Id of code generator
*
* @return {String}|null
* */
getCode : function(model, v)
{
var id = v || this.currentGenerator,
generator = this.generators[id];
return generator ? generator.build(model) : null;
},
/**
* Update editor content
* @param {EditorInteface} editor Editor
* @param {String} code Code value
*
* @return void
* */
updateEditor : function(editor, code)
{
editor.setContent(code);
},
};
return CodeManager;
});

57
bundle/code_manager/model/CodeMirrorEditor.js

@ -0,0 +1,57 @@
define(['backbone',
'text!../../../libs/codemirror/lib/codemirror.css',
'../../../libs/codemirror/lib/codemirror',
'../../../libs/codemirror/mode/htmlmixed/htmlmixed',
'../../../libs/codemirror/mode/css/css',
'../../../libs/codemirror/lib/util/formatting'
],
function(Backbone, CodeMirrorStyle, CodeMirror, htmlMode, cssMode, formatting ) {
/**
* @class CodeViewer
* */
return Backbone.Model.extend({
defaults: {
input : '',
label : '',
codeName : '',
theme : '',
readOnly : true,
lineNumbers : true,
},
/** @inheritdoc */
getId : function()
{
return 'CodeMirror';
},
/** @inheritdoc */
init: function(el)
{
this.editor = CodeMirror.fromTextArea(el, {
dragDrop : false,
lineNumbers : this.get('lineNumbers'),
readOnly : this.get('readOnly'),
mode : this.get('codeName'),
theme : this.get('theme'),
});
return this;
},
/** @inheritdoc */
setContent : function(v)
{
if(!this.editor)
return;
this.editor.setValue(v);
if(this.editor.autoFormatRange){
CodeMirror.commands.selectAll(this.editor);
this.editor.autoFormatRange(this.editor.getCursor(true), this.editor.getCursor(false) );
CodeMirror.commands.goDocStart(this.editor);
}
},
});
});

44
bundle/code_manager/model/CssGenerator.js

@ -0,0 +1,44 @@
define(['backbone'],
function (Backbone) {
/**
* @class CssGenerator
* */
return Backbone.Model.extend({
/** @inheritdoc */
getId : function()
{
return 'css';
},
/** @inheritdoc */
build: function(model)
{
var coll = model.get('components') || model,
code = '';
coll.each(function(m){
var css = m.get('style'),
cln = m.get('components'); // Children
if(css && Object.keys(css).length !== 0){
code += '#' + m.cid + '{';
for(var prop in css)
if(css.hasOwnProperty(prop))
code += prop + ': ' + css[prop] + ';';
code += '}';
}
if(cln.length)
code += this.build(cln);
}, this);
return code;
},
});
});

31
bundle/code_manager/model/EditorInterface.js

@ -0,0 +1,31 @@
define(function() {
/**
* @class EditorInterface
* */
function EditorInterface() {}
EditorInterface.prototype = {
/**
* Get id
*
* @return {String}|{Integer}
* */
getId : function(){},
/**
* Set content
* @param {String} str
*
* */
setContent : function(str){},
/**
* Init editor
* @param {Object} el DOM element
* */
init : function(el){},
};
return EditorInterface;
});

26
bundle/code_manager/model/GeneratorInterface.js

@ -0,0 +1,26 @@
define(function() {
/**
* @class GeneratorInterface
* */
function GeneratorInterface() {}
GeneratorInterface.prototype = {
/**
* Get id
*
* @return {String}|{Integer}
* */
getId : function(){},
/**
* Generate code from model
* @param {Backbone.Model} model
*
* @return {String}
* */
build : function(model){},
};
return GeneratorInterface;
});

39
bundle/code_manager/model/HtmlGenerator.js

@ -0,0 +1,39 @@
define(['backbone'],
function (Backbone) {
/**
* @class HtmlGenerator
* */
return Backbone.Model.extend({
/** @inheritdoc */
getId : function(){
return 'html';
},
/** @inheritdoc */
build: function(model){
var coll = model.get('components') || model,
code = '';
coll.each(function(m){
var tag = m.get('tagName'), // Tag name
attr = '', // Attributes string
cln = m.get('components'); // Children
_.each(m.get('attributes'),function(value, prop){
attr += value && prop!='style' ? ' ' + prop + '="' + value + '" ' : '';
});
code += '<'+tag+' id="'+m.cid+'"' + attr + '>' + m.get('content');
if(cln.length)
code += this.build(cln);
code += '</'+tag+'>';
}, this);
return code;
},
});
});

40
bundle/code_manager/model/JsonGenerator.js

@ -0,0 +1,40 @@
define(['backbone'],
function (Backbone) {
/**
* @class JsonGenerator
* */
return Backbone.Model.extend({
/** @inheritdoc */
getId : function()
{
return 'json';
},
/** @inheritdoc */
build: function(model)
{
var json = model.toJSON();
// Avoid jshint 'loopfunc' error
_.each(json,function(v, attr){
var obj = json[attr];
if(obj instanceof Backbone.Model){
json[attr] = this.build(obj);
}else if(obj instanceof Backbone.Collection){
var coll = obj;
json[attr] = [];
if(coll.length){
coll.each(function (el, index) {
json[attr][index] = this.build(el);
}, this);
}
}
}, this);
return json;
},
});
});

4
bundle/code_manager/template/editor.html

@ -0,0 +1,4 @@
<div class="<%= pfx %>editor" id="<%= pfx %><%= codeName %>">
<div id="<%= pfx %>title"><%= label %></div>
<div id="<%= pfx %>code"></div>
</div>

25
bundle/code_manager/view/EditorView.js

@ -0,0 +1,25 @@
define(['backbone', 'text!./../template/editor.html'],
function (Backbone, vTemplate) {
/**
* @class EditorView
* */
return Backbone.View.extend({
template: _.template(vTemplate),
initialize: function(o){
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
},
render : function(){
var obj = this.model.toJSON();
obj.pfx = this.pfx;
this.$el.html( this.template(obj) );
this.$el.attr('class', this.pfx + 'editor-c');
this.$el.find('#'+this.pfx+'code').html(this.model.get('input'));
return this;
},
});
});

25
bundle/commands/config/config.js

@ -0,0 +1,25 @@
define(function () {
return {
ESCAPE_KEY : 27,
stylePrefix : 'com-',
defaults : [],
// Editor model
em : null,
// If true center new first-level components
firstCentered : true,
// If true the new component will created with 'height', else 'min-height'
newFixedH : false,
// Minimum height (in px) of new component
minComponentH : 50,
// Minimum width (in px) of component on creation
minComponentW : 50,
};
});

90
bundle/commands/main.js

@ -0,0 +1,90 @@
define(function(require) {
/**
* @class Commands
* @param {Object} Configurations
*
* @return {Object}
* */
function Commands(config)
{
var c = config || {},
defaults = require('./config/config'),
AbsCommands = require('./view/CommandAbstract');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.commands = {};
this.config = c;
this.Abstract = AbsCommands;
this.defaultCommands = {};
this.defaultCommands['select-comp'] = require('./view/SelectComponent');
this.defaultCommands['create-comp'] = require('./view/CreateComponent');
this.defaultCommands['delete-comp'] = require('./view/DeleteComponent');
this.defaultCommands['resize-comp'] = require('./view/ResizeComponent');
this.defaultCommands['image-comp'] = require('./view/ImageComponent');
this.defaultCommands['move-comp'] = require('./view/MoveComponent');
this.defaultCommands['text-comp'] = require('./view/TextComponent');
this.defaultCommands['insert-var'] = require('./view/InsertCustom');
this.defaultCommands['export-template'] = require('./view/ExportTemplate');
this.defaultCommands['sw-visibility'] = require('./view/SwitchVisibility');
this.defaultCommands['open-layers'] = require('./view/OpenLayers');
this.defaultCommands['open-sm'] = require('./view/OpenStyleManager');
this.config.model = this.config.em.get('Canvas');
}
Commands.prototype = {
/**
* Add new command
* @param {String} id
* @param {Object} obj
*
* @return this
* */
add : function(id, obj)
{
delete obj.initialize;
this.commands[id] = this.Abstract.extend(obj);
return this;
},
/**
* Get command
* @param {String} id
*
* @return Command
* */
get : function(id)
{
var el = this.commands[id];
if(typeof el == 'function'){
el = new el(this.config);
this.commands[id] = el;
}
return el;
},
/**
* Load default commands
*
* @return this
* */
loadDefaultCommands : function()
{
for (var id in this.defaultCommands) {
this.add(id, this.defaultCommands[id]);
}
return this;
},
};
return Commands;
});

13
bundle/commands/model/Command.js

@ -0,0 +1,13 @@
define([ 'backbone'],
function (Backbone) {
/**
* @class Command
* */
return Backbone.Model.extend({
defaults :{
id : '',
}
});
});

11
bundle/commands/model/Commands.js

@ -0,0 +1,11 @@
define([ 'backbone','./Command'],
function (Backbone, Command) {
/**
* @class Commands
* */
return Backbone.Collection.extend({
model: Command,
});
});

51
bundle/commands/view/CommandAbstract.js

@ -0,0 +1,51 @@
define(['backbone'],
function(Backbone) {
/**
* @class CommandAbstract
* */
return Backbone.View.extend({
/**
* Initialize method that can't be removed
* @param {Object} o Options
* */
initialize: function(o) {
this.editorModel = this.em = o.em || {};
this.canvasId = o.canvasId || '';
this.wrapperId = o.wrapperId || '';
this.pfx = o.stylePrefix;
this.hoverClass = this.pfx + 'hover';
this.badgeClass = this.pfx + 'badge';
this.plhClass = this.pfx + 'placeholder';
this.setElement('#' + this.canvasId);
this.$canvas = this.$el;
this.$wrapper = $('#' + this.wrapperId);
this.init(o);
},
/**
* Callback triggered after initialize
* @param {Object} o Options
* */
init: function(o){},
/**
* Method that run command
* @param {Object} em Editor model
* @param {Object} sender Button sender
* */
run: function(em, sender) {
console.warn("No run method found");
},
/**
* Method that stop command
* @param {Object} em Editor model
* @param {Object} sender Button sender
* */
stop: function(em, sender) {
console.warn("No stop method found");
}
});
});

269
bundle/commands/view/CreateComponent.js

@ -0,0 +1,269 @@
define(['backbone','./SelectPosition'],
function(Backbone, SelectPosition) {
/**
* @class CreateComponent
* */
return _.extend({},SelectPosition,{
newElement : null,
tempComponent: { style:{} },
init: function(opt) {
SelectPosition.init.apply(this, arguments);
_.bindAll(this,'startDraw','draw','endDraw','rollback');
this.config = opt;
this.heightType = this.config.newFixedH ? 'height' : 'min-height';
},
/**
* Returns creation placeholder
*
* @return {Object}
* */
getCreationPlaceholder: function()
{
return this.newElem;
},
/**
* Removes creation placeholder
*
* @return void
* */
removeCreationPlaceholder: function()
{
this.newElem.remove();
},
/**
* Start with enabling to select position and listening to start drawning
* @return void
* */
enable: function()
{
SelectPosition.enable.apply(this, arguments);
this.$el.css('cursor','crosshair');
this.enableToDraw();
},
/**
* Enable user to draw components
*
* @return void
* */
enableToDraw: function()
{
this.$el.on('mousedown', this.startDraw);
this.$el.disableSelection(); //Disable text selection
},
/**
* Start drawing component
* @param {Object} e Event
*
* @return void
* */
startDraw : function(e)
{
e.preventDefault();
this.stopSelectPosition(); //Interrupt selecting position
this.tempComponent = { style: {} }; //Reset the helper
this.isDragged = false;
this.beforeDraw(this.tempComponent);
this.getPositionPlaceholder().addClass('change-placeholder'); //Change color of the position placeholder
this.newElemOrig = { top : e.pageY, left: e.pageX };
this.newElem = $('<div>', {class: "tempComp"}).css(this.newElemOrig); //Create helper element with initial position
this.newElem.data('helper',1);
$('body').append(this.newElem); //Show helper component
this.parentElem=this.newElem.parent(); //For percent count
this.targetC = this.outsideElem;
$(document).mousemove(this.draw);
$(document).mouseup(this.endDraw);
$(document).keypress(this.rollback);
},
/**
* While drawing the component
* @param {Object} e Event
*
* @return void
* */
draw: function(e)
{
this.isDragged = true;
this.updateComponentSize(e);
},
/**
* End drawing component
* @param {Object} e Event
*
* @return void
* */
endDraw : function(e)
{
$(document).off('mouseup', this.endDraw);
$(document).off('mousemove', this.draw);
$(document).off('keypress',this.rollback);
var model = {};
if(this.isDragged){ //Only if the mouse was moved
this.updateComponentSize(e);
this.setRequirements(this.tempComponent);
model = this.create(null,this.tempComponent,this.posIndex,this.posMethod);
}
if(this.getPositionPlaceholder())
this.getPositionPlaceholder().removeClass('change-placeholder'); //Turn back the original color of the placeholder
this.startSelectPosition(); //Return with selecting new position
this.removeCreationPlaceholder(); //Remove the element used for size indication
this.afterDraw(model);
},
/**
* Create component
* @param {Object} target DOM of the target element which to push new component
* @param {Object} component New component to push
* @param {Integer} posIndex Index inside the collection, 0 if no children inside
* @param {String} method Before or after of the children
*
* @return {Object} Created model
* */
create: function(target, component, posIndex, method)
{
var index = posIndex || 0;
if(this.posTargetCollection && this.posTargetModel.get('droppable')){
//Check config parameters for center in wrapper
if(this.config.firstCentered && (this.el == this.posTargetEl.get(0)) ){
component.style.margin = '0 auto';
}
if(this.nearToFloat()) //Set not in flow if the nearest is too
component.style.float = 'left';
this.beforeCreation(component);
var model = this.posTargetCollection.add(component, { at: index, silent:false });
this.afterCreation(model);
return model;
}else
console.warn("Invalid target position");
},
/**
* Check and set basic requirements for the component
* @param {Object} component New component to be created
* @return {Object} Component updated
* */
setRequirements: function(component)
{
var c = this.config;
if(component.style.width.replace(/\D/g,'') < c.minComponentW) //Check min width
component.style.width = c.minComponentW +'px';
if(component.style[this.heightType].replace(/\D/g,'') < c.minComponentH) //Check min height
component.style[this.heightType] = c.minComponentH +'px';
if(c.newFixedH) //Set overflow in case of fixed height
component.style.overflow = 'auto';
if(!this.absoluteMode){
delete component.style.left;
delete component.style.top;
}else
component.style.position = 'absolute';
return component;
},
/**
* Update new component size while drawing
* @param {Object} e Event
*
* @return void
* */
updateComponentSize : function (e)
{
var newLeft = e.pageX;
var newTop = e.pageY;
var startLeft = this.newElemOrig.left;
var startTop = this.newElemOrig.top;
var newWidth = newLeft - startLeft;//$(this.newElem).offset().left
var newHeight = newTop - startTop;//$(this.newElem).offset().top
if (newLeft < this.newElemOrig.left) {
startLeft = newLeft;
newWidth = this.newElemOrig.left - newLeft;
}
if (newTop < this.newElemOrig.top) {
startTop = newTop;
newHeight = this.newElemOrig.top - newTop;
}
newWidth = this.absoluteMode ? (newWidth/this.parentElem.width()*100+"%") : newWidth+'px';
this.newElem[0].style.left = startLeft+'px';
this.newElem[0].style.top = startTop+'px';
this.newElem[0].style.width = newWidth;
this.newElem[0].style['min-height'] = newHeight+'px';
this.tempComponent.style.width = newWidth;
this.tempComponent.style[this.heightType] = newHeight+"px";
this.tempComponent.style.left = startLeft + "px";
this.tempComponent.style.top = startTop + "px";
},
/**
* Used to bring the previous situation before event started
* @param {Object} e Event
* @param {Boolean} forse Indicates if rollback in anycase
*
* @return void
* */
rollback: function(e, force)
{
var key = e.which || e.keyCode;
if(key == this.config.ESCAPE_KEY || force){
this.isDragged = false;
this.endDraw();
}
return;
},
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
*
* @return void
* */
beforeDraw: function(component){
component.editable = false;//set this component editable
},
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
*
* @return void
* */
afterDraw: function(model){},
/**
* This event is triggered just before a create operation
* @param {Object} component Object component before creation
*
* @return void
* */
beforeCreation: function(component){},
/**
* This event is triggered at the end of a create operation
* @param {Object} model Component model created
*
* @return void
* */
afterCreation: function(model){},
/** Run method
* */
run: function(){
this.enable();
},
/** Stop method
* */
stop: function(){
this.removePositionPlaceholder(); //Removes placeholder from eventSelectPosition
this.$el.css('cursor',''); //Changes back aspect of the cursor
this.$el.unbind(); //Removes all attached events
}
});
});

59
bundle/commands/view/DeleteComponent.js

@ -0,0 +1,59 @@
define(['backbone', './SelectComponent'],
function(Backbone, SelectComponent) {
/**
* @class DeleteComponent
* */
return _.extend({},SelectComponent,{
init: function(o){
this.hoverClass = this.pfx + 'hover-delete';
this.badgeClass = this.pfx + 'badge-red';
},
enable: function(){
if(!this.$el.length)
this.$el = $('#' + this.canvasId);
var that = this;
this.$el.find('*').mouseover(function (e){
e.stopPropagation();
if($(this).data('model').get('removable')){ //Show badge if possible
$(this).addClass(that.hoverClass);
that.attachBadge(this);
}
}).mouseout(function (e){ //hover out
e.stopPropagation();
$(this).removeClass(that.hoverClass);
if(that.badge) //Hide badge if possible
that.badge.css({ left: -1000, top:-1000 });
}).click(function(e){
that.onSelect(e,this); //Callback on select
});
},
/**
* Say what to do after the component was selected
* @param Event
* @param Object Selected element
* */
onSelect: function(e, el){
e.stopPropagation();
var $selected = $(el);
if(!$selected.data('model').get('removable')) //Do nothing in case can't remove
return;
$selected.data('model').destroy();
this.removeBadge();
this.clean();
},
/**
* Updates badge label
* @param Object Model
* @return void
* */
updateBadgeLabel: function (model){
this.badge.html( 'Remove '+model.getName() );
},
});
});

73
bundle/commands/view/ExportTemplate.js

@ -0,0 +1,73 @@
define(function() {
/**
* @class ExportTemplate
* */
return {
run: function(em, sender){
this.sender = sender;
this.components = em.get('Canvas').getWrapper().get('components');
this.modal = em.get('Modal') || null;
this.cm = em.get('CodeManager') || null;
this.enable();
},
/**
* Build editor
* @param {String} codeName
* @param {String} theme
* @param {String} label
*
* @return {Object} Editor
* */
buildEditor: function(codeName, theme, label)
{
if(!this.codeMirror)
this.codeMirror = this.cm.getEditor('CodeMirror');
var $input = $('<textarea>'),
editor = this.codeMirror.clone().set({
label : label,
codeName : codeName,
theme : theme,
input : $input[0],
}),
$editor = new this.cm.EditorView({
model : editor,
config : this.cm.config
}).render().$el;
editor.init( $input[0] );
return { el: editor, $el: $editor };
},
enable: function()
{
if(!this.$editors){
var oHtmlEd = this.buildEditor('htmlmixed', 'codepen codepen-html', 'HTML2'),
oCsslEd = this.buildEditor('css', 'codepen codepen-css', 'CSS2');
this.htmlEditor = oHtmlEd.el;
this.cssEditor = oCsslEd.el;
this.$editors = $('<div>');
this.$editors.append(oHtmlEd.$el).append(oCsslEd.$el);
}
if(this.modal){
this.modal.setTitle('Export template');
this.modal.setContent(this.$editors);
this.modal.show();
}
this.htmlEditor.setContent( this.cm.getCode(this.components, 'html') );
this.cssEditor.setContent( this.cm.getCode(this.components, 'css') );
if(this.sender)
this.sender.set('active',false);
},
stop: function(){}
};
});

39
bundle/commands/view/ImageComponent.js

@ -0,0 +1,39 @@
define(['backbone','./InsertCustom'],
function(Backbone, InsertCustom) {
/**
* @class ImageComponent
* */
return _.extend({}, InsertCustom, {
/**
* Trigger before insert
* @param {Object} object
*
* @return void
* */
beforeInsert: function(object){
object.type = 'image';
object.style = {};
if (!this.nearToFloat()) {
object.style.display = 'block';
}
if (this.config.firstCentered && (this.el == this.posTargetEl.get(0)) ) {
object.style.margin = '0 auto';
}
},
/**
* Trigger after insert
* @param {Object} model Model created after insert
*
* @return void
* */
afterInsert: function(model){
model.trigger('dblclick');
if(this.sender)
this.sender.set('active',false);
},
});
});

79
bundle/commands/view/InsertCustom.js

@ -0,0 +1,79 @@
define(['backbone', './SelectPosition'],
function(Backbone, SelectPosition) {
/**
* @class InsertCustom
* */
return _.extend({}, SelectPosition, {
/**
* Run method
* */
run: function(em, sender){
this.enable();
this.sender = sender;
this.opt = sender.get('options') || {};
this.content = this.opt.content;
},
enable: function(){
SelectPosition.enable.apply(this, arguments);
_.bindAll(this,'insertComponent');
this.$el.on('click', this.insertComponent);
},
/**
* Start insert event
*
* @return void
* */
insertComponent: function(){
this.$el.off('click', this.insertComponent);
this.stopSelectPosition();
this.removePositionPlaceholder();
var object = this.buildContent();
this.beforeInsert(object);
var model = this.posTargetCollection.add(object, { at: this.posIndex, silent:false });
if(this.opt.terminateAfterInsert && this.sender){
this.sender.set('active',false);
//if(this.senderBtn.model.get('parentModel'))
//this.senderBtn.model.get('parentModel').set('active', false);
}else
this.enable();
this.afterInsert(model, this);
},
/**
* Trigger before insert
* @param {Object} obj
*
* @return void
* */
beforeInsert: function(obj){},
/**
* Trigger after insert
* @param {Object} model Model created after insert
*
* @return void
* */
afterInsert: function(model){},
/**
* Create different object, based on content, to insert inside canvas
*
* @return {Object}
* */
buildContent: function(){
var result = {};
if(typeof this.content === 'string'){
result = {
content : this.content,
tagName : 'span',
};
}else if(typeof this.content === 'object'){
result = this.content;
}
return result;
},
});
});

186
bundle/commands/view/MoveComponent.js

@ -0,0 +1,186 @@
define(['backbone', './SelectComponent','./SelectPosition'],
function(Backbone, SelectComponent, SelectPosition) {
/**
* @class MoveComponent
* */
return _.extend({},SelectComponent, SelectPosition,{
init: function(o){
_.bindAll(this,'startMove','onMove','endMove','rollback','selectingPosition','itemLeft');//to mantein 'this' context
this.opt = o;
this.hoverClass = this.pfx + 'hover-move';
this.badgeClass = this.pfx + 'badge-yellow';
},
enable: function(){
if(!this.$el.length){
this.$el = $('#' + this.canvasId);
this.$canvas = this.$el;
this.canvasTop = this.$canvas.offset().top;
this.canvasLeft = this.$canvas.offset().left;
}
this.$el.css('cursor','move');
this.$el.on('mousedown', this.startMove);
this.startSelectComponent();
this.$el.disableSelection(); //Avoid strange moving behavior
},
/** Highlight component when pointer is over it
* @param Event
* @param Object Component
* @return void
* */
highlightComponent: function(e, el){
e.stopPropagation();
if($(el).data('model').get('movable')){ //Show badge if possible
$(el).addClass(this.hoverClass);
this.attachBadge(el);
}
},
/** Say what to do after the component was selected
* - Method from selectComponent
* @param Event
* @param Object Selected element
* */
onSelect: function(e,el){},
/** Picking component to move
* @param event
* */
startMove: function(e, el){
this.moved = false;
if( !$(e.target).data('model').get('movable') ){ return; } //In case the component selected is not movable
this.$el.off('mousedown', this.startMove);
this.stopSelectComponent(e);
this.$selectedEl = $(e.target);
this.freezeComponent(this.$selectedEl);
this.helperObj = $('<div>', {class: "tempComp"}).css({ //HELPER gray box
top : (e.pageY - this.canvasTop) + this.$canvas.scrollTop(),
left: (e.pageX - this.canvasLeft) + this.$canvas.scrollLeft(),
width: $(e.target).width(),
height: $(e.target).height(),
position : 'absolute',
'pointer-events': 'none', //disable events for the element
}).data('helper',1).appendTo(this.$el);
this.startSelectPosition();
this.$el.on('mousemove',this.onMove);
$(document).on('mouseup',this.endMove);
$(document).on('keypress',this.rollback);
},
/** During move
* @param event */
onMove: function(e){
this.moved = true;
var relativeY = (e.pageY - this.canvasTop) + this.$canvas.scrollTop();
var relativeX = (e.pageX - this.canvasLeft) + this.$canvas.scrollLeft();
this.helperObj[0].style.top = (relativeY)+'px';
this.helperObj[0].style.left = (relativeX)+'px';
},
/** Leave component
* @param event */
endMove: function(e){
this.$el.off('mousemove',this.onMove);
$(document).off('mouseup', this.endMove);
$(document).off('keypress', this.rollback);
this.helperObj.remove();
this.removePositionPlaceholder();
this.stopSelectPosition();
//this.highlightComponent(e,el) after end of move
if(this.moved)
this.move(null, this.$selectedEl, this.posIndex, this.posMethod);
this.unfreezeComponent(this.$selectedEl);
this.enable();
},
/** Move component to new position
* @param object Component to move
* @param object Target component
* @param int Indicates the position inside the collection
* @param string Before of after component
*
* @return void
* */
move: function(target, el, posIndex, posMethod){
var index = posIndex || 0;
var model = el.data("model");
var collection = model.collection;
var targetCollection = this.posTargetCollection;
var targetModel = this.posTargetModel;
if(targetCollection && targetModel.get('droppable')){
var modelTemp = targetCollection.add({css:{}}, { at: index });
var modelRemoved = collection.remove(model);
targetCollection.add(modelRemoved, { at: index });
targetCollection.remove(modelTemp);
}else
console.warn("Invalid target position");
},
/** Make component untouchable
* @param object Component
* @return void
* */
freezeComponent: function($component){
$component.css({'pointer-events':'none'});
$component.addClass('freezed');
},
/** Make component touchable
* @param object Component
* @return void
* */
unfreezeComponent: function($component){
$component.css({'pointer-events':'auto'});
$component.removeClass('freezed');
},
/** Used to bring the previous situation before start moving the component
* @param Event
* @param Bool Indicates if rollback in anycase
* @return void
* */
rollback: function(e, force){
var key = e.which || e.keyCode;
if(key == this.opt.ESCAPE_KEY || force){
this.moved = false;
this.endMove();
}
return;
},
/** Closing method
* */
last: function(){
this.placeholder.remove();
this.placeholderStart.remove();
this.helperObj.remove();
this.$el.off('mousemove',this.move);
$(document).off('mouseup', this.endMove);
$(document).off('keypress', this.rollback);
},
/* Run method */
run: function(){
this.enable();
this.active = true;
},
/* Stop method */
stop: function(){
this.stopSelectComponent();
this.$el.css('cursor','');//changes back aspect of the cursor
this.$el.unbind();//removes all attached events
this.active = false;
}
});
});

35
bundle/commands/view/OpenLayers.js

@ -0,0 +1,35 @@
define(['Navigator'], function(Layers) {
/**
* @class OpenStyleManager
* */
return {
run: function(em, sender)
{
if(!this.$layers){
var collection = em.get('Components').getComponent().get('components'),
config = em.get('Config'),
panels = em.get('Panels'),
lyStylePfx = config.layers.stylePrefix || 'nv-';
config.layers.stylePrefix = config.stylePrefix + lyStylePfx;
var layers = new Layers(collection, config.layers);
this.$layers = layers.render();
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({ id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
this.panel.set('appendContent', this.$layers).trigger('change:appendContent');
}
this.$layers.show();
},
stop: function()
{
if(this.$layers)
this.$layers.hide();
}
};
});

36
bundle/commands/view/OpenStyleManager.js

@ -0,0 +1,36 @@
define(['StyleManager'], function(StyleManager) {
/**
* @class OpenStyleManager
* */
return {
run: function(em, sender)
{
if(!this.$sm){
var config = em.get('Config'),
panels = em.get('Panels'),
smStylePfx = config.styleManager.stylePrefix || 'sm-';
config.styleManager.stylePrefix = config.stylePrefix + smStylePfx;
config.styleManager.target = em;
var sm = new StyleManager(config.styleManager);
this.$sm = sm.render();
if(!panels.getPanel('views-container'))
this.panel = panels.addPanel({ id: 'views-container'});
else
this.panel = panels.getPanel('views-container');
this.panel.set('appendContent', this.$sm).trigger('change:appendContent');
}
this.$sm.show();
},
stop: function()
{
if(this.$sm)
this.$sm.hide();
}
};
});

57
bundle/commands/view/ResizeComponent.js

@ -0,0 +1,57 @@
define(['backbone', 'jqueryUi', './MoveComponent'],
function(Backbone, jqueryUi, MoveComponent) {
/**
* @class ResizeComponent
* */
return _.extend({}, MoveComponent,{
enable: function(){
var $this = this;
this.startSelectComponent();
this.$el.find('*').resizable({
containment: 'parent',
start: function(event,ui){
ui.element[0].style.height = ui.element.height()+'px';
ui.element.css({'min-height':'', 'min-width':'' });
},
stop: function(event,ui){
ui.element.css('overflow','auto');
$this.updateModel(ui);
}
});
},
/**
* Update model of resized element
* @param object Component model
* */
updateModel: function(el){
var um = 'px';
var model = el.element.data("model");
delete model.get('style')['min-height']; //resize event removes fixed measures
delete model.get('style')['min-width'];
model.get('style').height = el.size.height+um; //update with new height and width
model.get('style').width = el.size.width+um;
model.get('style').overflow = 'auto';
},
/**
* Run method
* */
run: function(){
this.enable();
this.active = true;
},
/**
* Stop method
* */
stop: function(){
this.stopSelectComponent();
this.$el.find('.ui-resizable').resizable("destroy");
this.$el.unbind();//removes all attached events
this.active = false;
}
});
});

165
bundle/commands/view/SelectComponent.js

@ -0,0 +1,165 @@
define(function() {
/**
* @class SelectComponent
* */
return {
enable: function(){
this.startSelectComponent();
},
/** Start select component event
* @return void
* */
startSelectComponent: function(){
var that = this;
if(!this.$el.length)
this.$el = $('#' + this.canvasId);
this.$el.find('*').on('mouseover',function(e){ that.highlightComponent(e,this); })
.on('mouseout' ,function(e){ that.removeHighlightComponent(e,this); })
.on('click' ,function(e){ that.selectComponent(e,this); });
this.selEl = this.$el.find('*');
},
/** Stop select component event
* @param Event
* @return void
* */
stopSelectComponent: function(e){
if(this.selEl)
this.selEl.trigger('mouseout').off('mouseover mouseout click');
this.selEl = null;
},
/** Highlight component when pointer is over it
* @param Event
* @param Object Component
* @return void
* */
highlightComponent: function(e, el){
e.stopPropagation();
$(el).addClass(this.hoverClass);
this.attachBadge(el);
},
/** Remove highlight from component
* @param Event
* @param Object Component
* @return void
* */
removeHighlightComponent: function(e, el){
e.stopPropagation();
$(el).removeClass(this.hoverClass);
if(this.badge) //Hide badge if possible
this.badge.css({ left: -10000, top:-10000 }); //TODO HIDE
},
/** Select highlighted component
* @param Event
* @param Object Component
* @return void
* */
selectComponent: function(e, el){
this.onSelect(e,el); //Callback on select
},
/** Say what to do after the component was selected
* @param Event
* @param Object Selected element
* */
onSelect: function(e,el){
e.stopPropagation();
if(this.$selected) //Check if already selected before
this.$selected.removeClass('selected-component');
this.$selected = $(el).addClass('selected-component');
if(this.$selected.data('model')){
// Generates too much recursions with JsonGenerator
//this.$selected.data('model').set('previousModel',this.editorModel.get('selectedComponent'));
this.editorModel.set('selectedComponent',this.$selected.data('model')); //Update selected component
this.$selected.data('model').set('status','selected');
}
},
/** Removes all highlighting effects on components
* @return void
* */
clean: function(){
this.$el.find('*').removeClass(this.hoverClass);
},
/** Attach badge to component
* @param Object Component
* @return void
* */
attachBadge: function(el){
var model = $(el).data("model");
if(!model || !model.get('badgable'))
return;
if(!this.badge)
this.createBadge();
var badgeH = this.badge.outerHeight();
this.updateBadgeLabel(model);
var $el = $(el);
if(!this.wrapper)
this.wrapper = $('#wrapper');
if(!this.wrapperTop)
this.wrapperTop = this.wrapper.offset() ? this.wrapper.offset().top : 0;
if(!this.wrapperLeft)
this.wrapperLeft= this.wrapper.offset() ? this.wrapper.offset().left : 0;
var relativeT = ($el.offset().top - this.wrapperTop) + this.wrapper.scrollTop();
var relativeL = ($el.offset().left- this.wrapperLeft) + this.wrapper.scrollLeft();
if( (relativeT-badgeH) > this.wrapperTop) //If it's possible set badge to top
relativeT -= badgeH;
this.badge.css({ left: relativeL, top:relativeT });
},
/** Create badge for the component
* @return void
* */
createBadge: function (){
this.badge = $('<div>', {class: this.badgeClass + " no-dots"}).appendTo('#' + this.wrapperId);
},
/** Remove badge
* @return void
* */
removeBadge: function (){
if(this.badge){
this.badge.remove();
delete this.badge;
}
},
/** Updates badge label
* @param Object Model
* @return void
* */
updateBadgeLabel: function (model){
if(model)
this.badge.html( model.getName() );
},
/** Run method
* */
run: function(){
this.enable();
this.render();
this.active = true;
},
/** Stop method
* */
stop: function(){
if(this.editorModel.get('selectedComponent'))
this.editorModel.get('selectedComponent').set('status','');
this.$el.unbind(); //removes all attached events
if(this.$selected) //check if already selected before
this.$selected.removeClass('selected-component');
this.removeBadge();
this.clean();
this.$el.find('*').unbind('mouseover').unbind('mouseout').unbind('click');
this.editorModel.set('selectedComponent',null);
this.active = false;
}
};
});

375
bundle/commands/view/SelectPosition.js

@ -0,0 +1,375 @@
define(function() {
/**
* @class SelectPosition
* */
return {
init: function(opt) {
_.bindAll(this,'selectingPosition','itemLeft');
this.setElement('#'+this.wrapperId);
this.config = opt;
},
/**
* Returns position placeholder
*
* @return {Object} Placeholder
* */
getPositionPlaceholder: function()
{
return this.$plh;
},
/**
* Creates position placeholder
*
* @return {Object} Placeholder
* */
createPositionPlaceholder: function()
{
this.$plh = $('<div>', { class: this.plhClass + " no-dots" })
.css({'pointer-events':'none'}).data('helper',1);
this.$plh.append( $('<div>', { class: this.plhClass + "-int no-dots" } ) );
this.$plh.appendTo( this.$wp ); //Append helper to the canvas
return this.$plh;
},
enable: function()
{
if(!this.$el.length){
this.setElement('#'+this.wrapperId);
this.$el = $('#'+this.wrapperId);
}
this.$el.css('cursor','pointer'); //changes aspect of the cursor
this.startSelectPosition();
},
/**
* Start select position event
*
* @return void
* */
startSelectPosition: function()
{
this.isPointed = false;
this.$el.on('mousemove', this.selectingPosition);
},
/**
* Stop select position event
* @return void
* */
stopSelectPosition: function()
{
this.$el.off('mousemove',this.selectingPosition);
this.posTargetCollection = null;
this.posIndex = this.posMethod=='after' && this.cDim.length!==0 ? this.posIndex + 1 : this.posIndex; //Normalize
if(this.cDim){
this.posIsLastEl = this.cDim.length!==0 && this.posMethod=='after' && this.posIndex==this.cDim.length;
this.posTargetEl = (this.cDim.length===0 ? $(this.outsideElem) :
(!this.posIsLastEl ? $(this.cDim[this.posIndex][5]).parent() : $(this.outsideElem) ));
this.posTargetModel = this.posTargetEl.data("model");
this.posTargetCollection = this.posTargetEl.data("model-comp");
}
},
/**
* During event
* @param {Object} e Event
* */
selectingPosition: function(e)
{
this.isPointed = true;
if(!this.$wp){
this.$wp = $('#' + this.wrapperId);
this.wp = this.$wp[0];
}
var wpO = this.$wp.offset();
this.wpT = wpO.top;
this.wpL = wpO.left;
this.wpScT = this.$wp.scrollTop();
this.wpScL = this.$wp.scrollLeft();
if(!this.$plh)
this.createPositionPlaceholder();
this.rY = (e.pageY - this.wpT) + this.wpScT;
this.rX = (e.pageX - this.wpL) + this.wpScL;
this.entered(e);
this.updatePosition(this.rX, this.rY);
var actualPos = this.posIndex + ':' + this.posMethod; //save globally the new index
if(!this.lastPos || (this.lastPos != actualPos)){ //If there is a significant changes with mouse
this.updatePositionPlaceholder(this.posIndex, this.posMethod);
this.lastPos = actualPos;
}
},
/**
* Search where to put placeholder
* @param {Integer} posX X position of the mouse
* @param {Integer} posY Y position of the mouse
*
* @retun void
* */
updatePosition: function( posX, posY ){
this.posMethod = "before";
this.posIndex = 0;
var leftLimit = 0, xLimit = 0, dimRight = 0, yLimit = 0, xCenter = 0, yCenter = 0, dimDown = 0, dim = 0;
for(var i = 0; i < this.cDim.length; i++){
dim = this.cDim[i];
dimDown = dim[0] + dim[2];
yCenter = dim[0] + (dim[2] / 2); //Horizontal center
xCenter = dim[1] + (dim[3] / 2); //Vertical center
dimRight = dim[1] + dim[3];
if( (xLimit && dim[1] > xLimit) || (yLimit && yCenter > yLimit) ||
(leftLimit && dimRight < leftLimit)) //No need with this one if over the limit
continue;
if(!dim[4]){ //If it's not inFlow (like float element)
if( posY < dimDown)
yLimit = dimDown;
if( posX < xCenter){ //If mouse lefter than center
xLimit = xCenter;
this.posMethod = "before";
}else{
leftLimit = xCenter;
this.posMethod = "after";
}
this.posIndex = i;
}else{
this.posIndex = i;
if( posY < yCenter ){ //If mouse upper than center
this.posMethod = "before"; //Should place helper before
break; //No need to continue under inFlow element
}else
this.posMethod = "after";
}
}
if(this.posIndex == (this.cDim.length) && this.posMethod == 'after' ){
this.posIndex--;
}
},
/**
* Updates the position of the placeholder
* @param {Integer} index Index of the nearest child
* @param {String} method Before or after position
*
* @return void
* */
updatePositionPlaceholder: function(index, method){
var t = 0, l = 0, w = 0, h = 0,
marg = 2,
un = 'px',
margI = 5,
plh = this.$plh[0];
if( this.cDim[index] ){
var elDim = this.cDim[index];
if(!elDim[4]){
w = 'auto';
t = elDim[0] + marg;
h = elDim[2] - (marg * 2) + un;
l = (method == 'before') ? (elDim[1] - marg) : (elDim[1] + elDim[3] - marg);
}else{
w = elDim[3] + un;
t = (method == 'before') ? (elDim[0] - marg) : (elDim[0] + elDim[2] - marg);
l = elDim[1];
h = 'auto';
}
//t -= this.wpScT;
//l -= this.wpScL;
}else{
if(this.$targetEl){
var trg = this.$targetEl[0],
$eO = this.$targetEl.offset();
t = $eO.top - this.wpT + this.wpScT + margI;
l = $eO.left - this.wpL + this.wpScL + margI;
w = (parseInt(trg.offsetWidth) - margI * 2) + un;
h = 'auto';
}
}
plh.style.top = t + un;
plh.style.left = l + un;
if(w)
plh.style.width = w;
if(h)
plh.style.height = h;
},
/**
* Track inside which element pointer entered
* @param {Object} e Event
*
* @return void
* */
entered: function(e){
if( (!this.outsideElem || this.outsideElem != e.target) ){ //If I'm in the new element
this.outsideElem = e.target; //Set the element in which it's actually inside
this.$targetEl = $(e.target);
$(this.outsideElem).on('mouseleave',this.itemLeft);
this.cDim = this.getChildrenDim();
this.dimT = this.getTargetDim(e);
if( this.nearToBorders(e) && (e.target.parentNode!=this.wp.parentNode) ) //Avoid flickering
this.cDim = this.getChildrenDim(e.target.parentNode);
}else if( this.nearToBorders(e) && (e.target.parentNode!=this.wp.parentNode) ){ //Near to borders and parent is not the canvas
this.cDim = this.getChildrenDim(e.target.parentNode);
}else if( !(this.nearToBorders(e)) ){
this.cDim = this.getChildrenDim();
}
},
/**
* Check if pointer is near to the borders of the target
* @param {Object} e Event
*
* @return {Integer}
* */
nearToBorders: function(e){
var m = 7; //Limit in pixels for be near
if(!this.dimT)
return;
var dimT = this.dimT;
if(dimT[2] < 40)
m = 5;
if( ((dimT[0] + m) > this.rY) || (this.rY > (dimT[0] + dimT[2] - m)) ||
((dimT[1] + m) > this.rX) || (this.rX > (dimT[1] + dimT[3] - m)) ) //Check if the pointer is near
return 1;
else
return 0;
},
/**
* Check if pointer is near to the float component
*
* @return {Integer}
* */
nearToFloat: function()
{
var index = this.posIndex;
var isLastEl = this.posIsLastEl;
if(this.cDim.length !== 0 && (
(!isLastEl && !this.cDim[index][4]) ||
(this.cDim[index-1] && !this.cDim[index-1][4]) ||
(isLastEl && !this.cDim[index-1][4]) ) )
return 1;
else
return 0;
},
/**
* Returns dimension of the taget
* @param {Object} e Event
*
* @return {Array}
* */
getTargetDim: function(e)
{
var elT = e.target,
$el = $(elT);
return [ elT.offsetTop, elT.offsetLeft, $el.outerHeight(), $el.outerWidth() ];
},
/**
* Returns children and their dimensions of the target element,
* excluding text nodes and the move placeholder
* @param {Object} el Element
*
* @return {Array}
* */
getChildrenDim: function(el)
{
var dim = [];
var elToPars = el || this.outsideElem;
var isInFlow = this.isInFlow; //Assign method for make it work inside $.each
var $this = this; //Store context
$(elToPars.childNodes).each(function(){
var $el = $(this);
if(this.nodeName != '#text' && !$el.data('helper') ){ //Ignore text nodes and helpers
dim.push( [ this.offsetTop, this.offsetLeft, $el.outerHeight(), $el.outerWidth(), isInFlow($this, this), this ] );
}
});
return dim;
},
/**
* Track when I go ouside of the element (basically when the target changes)
* @param {Object} e Event
*
* @return void
* */
itemLeft: function(e)
{
$(this.outsideElem).off('mouseleave',this.itemLeft);
this.outsideElem = null;
this.$targetEl = null;
this.lastPos = null;
},
/**
* Returns true if the elements is in flow, or better is not in flow where
* for example the component is with float:left
* @param {Object} $this Context
* @param {Object} elm Element
*
* @return {Boolean}
* */
isInFlow: function($this, elm)
{
var $elm = $(elm), ch = -1;
if(!$elm.length)
return false;
if( ($elm.height() < ch) || !$this.okProps($elm) )
return false;
return true;
},
/**
* Returns true only if the element follow the standard flow
* @param {Object} $elm Element
*
* @return {Boolean}
* */
okProps: function($elm)
{
if ($elm.css('float')!=='none')
return false;
switch($elm.css('position')) {
case 'static': case 'relative': break;
default: return false;
}
switch ($elm.css('display')) {
case 'block': case 'list-item': case 'table': return true;
}
return false;
},
/**
* Removes position placeholder
*
* @param void
* */
removePositionPlaceholder: function()
{
if(this.$plh)
this.$plh.remove();
this.$plh = null;
},
/* Run method */
run: function(){
this.enable();
this.active = true;
},
/* Stop method */
stop: function(){
this.removePositionPlaceholder();
this.$el.css('cursor','');//changes back aspect of the cursor
this.$el.unbind();//removes all attached events
this.active = false;
}
};
});

17
bundle/commands/view/SwitchVisibility.js

@ -0,0 +1,17 @@
define(function() {
/**
* @class SwitchVisibility
* */
return {
run: function()
{
this.$canvas.addClass(this.pfx + 'dashed');
},
stop: function()
{
this.$canvas.removeClass(this.pfx + 'dashed');
}
};
});

44
bundle/commands/view/TextComponent.js

@ -0,0 +1,44 @@
define(['backbone', './CreateComponent'],
function(Backbone, CreateComponent) {
/**
* @class TextComponent
* */
return _.extend({}, CreateComponent, {
/**
* This event is triggered at the beginning of a draw operation
* @param {Object} component Object component before creation
*
* @return void
* */
beforeDraw: function(component){
component.type = 'text';
if(!component.style)
component.style = {};
component.style.padding = '10px';
},
/**
* This event is triggered at the end of a draw operation
* @param {Object} model Component model created
*
* @return void
* */
afterDraw: function(model){
if(!model.set)
return;
model.trigger('focus');
if(this.senderBtn)
this.senderBtn.set('active',false);
},
/**
* Run method
* */
run: function(em, sender){
this.enable();
this.senderBtn = sender;
},
});
});

49
bundle/config/require-config.js

@ -0,0 +1,49 @@
require.config({
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [ 'underscore', 'jquery' ],
exports: 'Backbone'
},
rte: {
deps: [ 'jquery' ],
exports: 'rte'
},
backboneUndo: {
deps: ['backbone'],
exports: 'backboneUndo'
},
keymaster: {
exports: 'keymaster'
},
},
paths: {
jquery: '../libs/jquery',
jqueryUi: '../libs/jquery-ui.min',
underscore: '../libs/underscore',
backbone: '../libs/backbone',
backboneUndo: '../libs/backbone-undo-min',
keymaster: '../node_modules/keymaster/keymaster',
text: '../libs/require-text',
Spectrum: '../libs/spectrum',
rte: '../libs/wysiwyg',
config: 'config/config',
},
packages : [
{ name: 'AssetManager', location: 'asset_manager', },
{ name: 'StyleManager', location: 'style_manager', },
{ name: 'StorageManager', location: 'storage_manager', },
{ name: 'Navigator', location: 'navigator', },
{ name: 'DomComponents', location: 'dom_components', },
{ name: 'RichTextEditor', location: 'rich_text_editor', },
{ name: 'ModalDialog', location: 'modal_dialog', },
{ name: 'CodeManager', location: 'code_manager', },
{ name: 'Commands', location: 'commands', },
{ name: 'Canvas', location: 'canvas', },
{ name: 'Panel', location: 'panel', }
]
});

60
bundle/dev.js

@ -0,0 +1,60 @@
require(['bundle/config/require-config.js'], function() {
require(['editor/main'],function (Grapes){
var grapes = new Grapes({
storageType: 'local',
remoteStorage: {
urlStore : 'http://test.localhost/wte/index.php',
urlLoad : 'http://test.localhost/wte/read.php',
paramsStore : { type:'homeTemplate',},
paramsLoad : { type:'homeTemplate',},
},
assetManager: {
storageType : '',
storeOnChange : true,
storeAfterUpload : true,
assets : [
{ type: 'image', src : 'http://placehold.it/350x250/78c5d6/fff/image1.jpg', date: '2015-01-01',height:350, width:250},
{ type: 'image', src : 'http://placehold.it/350x250/459ba8/fff/image2.jpg', date: '2015-02-01',height:350, width:250},
{ type: 'image', src : 'http://placehold.it/350x250/79c267/fff/image3.jpg', date: '2015-02-01',height:350, width:250},
{ type: 'image', src : 'http://placehold.it/350x250/c5d647/fff/image4.jpg', date: '2015-02-01',height:350, width:250},
{ type: 'image', src : 'http://placehold.it/350x250/f28c33/fff/image5.jpg', date: '2015-02-01',height:350, width:250},
{ type: 'image', src : 'http://placehold.it/350x250/e868a2/fff/image6.jpg', date: '2015-02-01',height:350, width:250},
{ type: 'image', src : 'http://placehold.it/350x250/cc4360/fff/image7.jpg', date: '2015-02-01',height:350, width:250},
]
},
styleManager : {},
defaultComponents: [ { style: {'width':'500px', 'height': '35px', 'margin':'0 auto',}, },
{ style: {'width':'500px', 'height': '35px', 'margin':'0 auto',}, },
{ style: {'width':'500px', 'height': '35px', 'margin':'0 auto',}, },
{ style: {'width':'400px', 'height': '300px', 'margin':'0 auto', 'padding':'5px'},
components: [{ style: {'width':'130px','height': '30px','float':'left'}},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '80px','float':'left'},},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '70px','float':'left'},},
{ style: {'width':'50px','height': '80px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'75px','height': '50px','clear':'both'}}]
},
{ style: {'width':'700px', 'height': '250px', 'margin':'0 auto'},
components: [{ style: {'width':'100px','height': '30px','float':'left'}},
{ style: {'width':'200px','height': '50px','float':'left'},},
{ style: {'width':'150px','height': '150px','float':'left'},}]
},
{ style: {'width':'500px', 'height': '150px', 'margin':'0 auto'}, }
],
});
grapes.render();
});
});

20
bundle/dom_components/config/config.js

@ -0,0 +1,20 @@
define(function () {
return {
stylePrefix : 'comp-',
wrapperId : 'wrapper',
component : {},
// Could be used for default components
components : {},
rte : {},
// Class for new image component
imageCompClass : 'fa fa-picture-o',
// Open assets manager on create of image component
oAssetsOnCreate : true,
};
});

46
bundle/dom_components/main.js

@ -0,0 +1,46 @@
define(function(require) {
/**
* @class Components
* @param {Object} Configurations
*
* @return {Object}
* */
function Components(config)
{
var c = config || {},
defaults = require('./config/config'),
Component = require('./model/Component'),
ComponentText = require('./model/ComponentText'),
ComponentImage = require('./model/ComponentImage'),
ComponentView = require('./view/ComponentView'),
ComponentImageView = require('./view/ComponentImageView'),
ComponentTextView = require('./view/ComponentTextView');
// Set default options
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.component = new Component(c.component);
var obj = {
model : this.component,
config : c,
};
this.ComponentView = new ComponentView(obj);
}
Components.prototype = {
render : function(){
return this.ComponentView.render().$el;
},
getComponent : function(){
return this.component;
},
};
return Components;
});

44
bundle/dom_components/model/Component.js

@ -0,0 +1,44 @@
define(['backbone','./Components'],
function (Backbone, Components) {
/**
* @class Component
* */
return Backbone.Model.extend({
defaults: {
tagName : 'div',
type : '',
editable : false,
removable : true,
movable : true,
droppable : true,
badgable : true,
status : '',
previousModel : '',
content : '',
style : {},
attributes : {},
},
initialize: function(options) {
this.defaultC = options.components || [];
this.components = new Components(this.defaultC);
this.set('components', this.components);
},
/**
* Get name of the component
*
* @return string
* */
getName: function(){
if(!this.name){
var id = this.cid.replace(/\D/g,''),
type = this.get('type');
this.name = type.charAt(0).toUpperCase() + type.slice(1) + 'Box' + id;
}
return this.name;
},
});
});

14
bundle/dom_components/model/ComponentImage.js

@ -0,0 +1,14 @@
define(['./Component'],
function (Component) {
/**
* @class ComponentImage
* */
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
src : '',
droppable : false,
}),
});
});

14
bundle/dom_components/model/ComponentText.js

@ -0,0 +1,14 @@
define(['./Component'],
function (Component) {
/**
* @class ComponentText
* */
return Component.extend({
defaults: _.extend({}, Component.prototype.defaults, {
content : '',
droppable : false,
}),
});
});

41
bundle/dom_components/model/Components.js

@ -0,0 +1,41 @@
define([ 'backbone', 'require'],
function (Backbone, require) {
/**
* @class Components
* */
return Backbone.Collection.extend({
initialize: function(models, opt){
this.model = function(attrs, options) {
var model;
switch(attrs.type){
case 'text':
if(!this.mComponentText)
this.mComponentText = require("./ComponentText");
model = new this.mComponentText(attrs, options);
break;
case 'image':
if(!this.mComponentImage)
this.mComponentImage = require("./ComponentImage");
model = new this.mComponentImage(attrs, options);
break;
default:
if(!this.mComponent)
this.mComponent = require("./Component");
model = new this.mComponent(attrs, options);
}
return model;
};
},
});
});

65
bundle/dom_components/view/ComponentImageView.js

@ -0,0 +1,65 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
/**
* @class ComponentImageView
* */
return ComponentView.extend({
tagName : 'img',
events : {
'dblclick' : 'openModal',
},
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
this.listenTo( this.model, 'change:src', this.updateSrc);
this.listenTo( this.model, 'dblclick', this.openModal);
this.classEmpty = this.config.stylePrefix + 'image-placeholder ' + this.config.imageCompClass;
if(!this.model.get('src'))
this.$el.attr('class', this.classEmpty);
if(this.config.modal)
this.modal = this.config.modal;
if(this.config.am)
this.am = this.config.am;
},
/**
* Update src attribute
*
* @return void
* */
updateSrc: function(){
this.$el.attr('src',this.model.get("src"));
},
/**
* Open dialog for image changing
* @param {Object} e Event
*
* @return void
* */
openModal: function(e){
var that = this;
if(this.modal && this.am){
this.modal.setTitle('Select image');
this.modal.setContent(this.am.render());
this.am.setTarget(this.model);
this.modal.show();
this.am.onSelect(function(){
that.modal.hide();
that.am.setTarget(null);
});
}
},
render: function() {
this.updateAttributes();
return this;
},
});
});

72
bundle/dom_components/view/ComponentTextView.js

@ -0,0 +1,72 @@
define(['backbone', './ComponentView'],
function (Backbone, ComponentView) {
/**
* @class ComponentTextView
* */
return ComponentView.extend({
events: {
'dblclick' : 'enableEditing',
},
initialize: function(o){
ComponentView.prototype.initialize.apply(this, arguments);
_.bindAll(this,'disableEditing');
this.listenTo( this.model, 'focus', this.enableEditing);
if(this.config.rte){
this.rte = this.config.rte;
}
},
/**
* Enable this component to be editable,
* load also the mini toolbar for quick editing
* @param Event
* */
enableEditing: function(e){
if(this.rte){
if(!this.$wrapper)
this.$wrapper = $('#'+this.config.wrapperId);
this.rte.bind(this, this.$wrapper);
}
$(document).on('mousedown', this.disableEditing); //Close edit mode
this.$el.on('mousedown', this.disablePropagation); //Avoid closing edit mode on component click
},
/**
* Disable this component to be editable
* @param Event
* */
disableEditing: function(e){
if(this.rte){
this.rte.unbind(this);
}
$(document).off('mousedown', this.disableEditing);
this.$el.off('mousedown',this.disablePropagation);
this.updateContents();
},
/** Isolate disable propagation method
* @param Event
* */
disablePropagation: function(e){
e.stopPropagation();
},
/**
* Update contents of the element
*
* @return void
**/
updateContents : function(){
this.model.set('content', this.$el.html());
},
render: function() {
this.updateAttributes();
this.$el.html(this.model.get('content'));
return this;
},
});
});

152
bundle/dom_components/view/ComponentView.js

@ -0,0 +1,152 @@
define(['backbone', './ComponentsView'],
function (Backbone, ComponentsView) {
/**
* @class ComponentView
* */
return Backbone.View.extend({
className : function(){ //load classes from model
return this.getClasses();
},
tagName: function(){ //load tagName from model
return this.model.get('tagName');
},
initialize: function(opt){
this.config = opt.config;
this.components = this.model.get('components');
this.attr = this.model.get("attributes");
this.classe = this.attr.class || [];
this.listenTo( this.model, 'destroy remove', this.remove);
this.listenTo( this.model, 'change:style', this.updateStyle);
this.listenTo( this.model, 'change:attributes', this.updateAttributes);
this.$el.data("model", this.model);
this.$el.data("model-comp", this.components);
},
/**
* Get classes from attributes.
* This method is called before initialize
*
* @return {Array}|null
* */
getClasses: function(){
var attr = this.model.get("attributes"),
classes = attr['class'] || [];
if(classes.length){
return classes.join(" ");
}else
return null;
},
/**
* Update attributes
*
* @return void
* */
updateAttributes: function(){
var attributes = {},
attr = this.model.get("attributes");
for(var key in attr) {
if(attr.hasOwnProperty(key))
attributes[key] = attr[key];
}
// Update src
if(this.model.get("src"))
attributes.src = this.model.get("src");
attributes.style = this.getStyleString();
this.$el.attr(attributes);
},
/**
* Update style attribute
*
* @return void
* */
updateStyle: function(){
this.$el.attr('style', this.getStyleString());
},
/**
* Return style string
*
* @return {String}
* */
getStyleString: function(){
var style = '';
this.style = this.model.get('style');
for(var key in this.style) {
if(this.style.hasOwnProperty(key))
style += key + ':' + this.style[key] + ';';
}
return style;
},
/**
* Update classe attribute
*
* @return void
* */
updateClasses: function(){
if(this.classe.length)
this.$el.attr('class', this.classe.join(" "));
},
/**
* Reply to event call
* @param object Event that generated the request
* */
eventCall: function(event){
event.viewResponse = this;
},
render: function() {
this.updateAttributes();
this.$el.html(this.model.get('content'));
var view = new ComponentsView({
collection : this.components,
config : this.config,
});
this.$components = view;
// With childNodes lets avoid wrapping 'div'
this.$el.append(view.render(this.$el).el.childNodes);
return this;
},
/** TODO DELETE
* Add new component to canvas
* @param Object Component added
* @param Object Collection
* @param Object Parameters
*
addComponent: function (component, collection, params) {
var viewObject = require('componentView'); //Set default view
if(component.get('editable')) //If editable component, change view
viewObject = require('componentTextView'); //Change view in case is editable
if(component.get('src')){ //If editable component, change view
viewObject = require('componentImageView'); //Change view in case is editable
}
var view = new viewObject({
model: component,
editorModel: this.editorModel,
});
if(params && (typeof params.at!='undefined') ){ //If i have index position change the way to append
if (params.at === 0){
this.$el.prepend(view.render().el);
}else{
this.$el.children().filter(function(){
return !$(this).data('helper');
}).eq(params.at-1).after(view.render().el);
//console.log("insert at "+params.at+" children: "+this.$el.children().filter(function() { return !$(this).data('helper');}).length);
}
}else
this.$el.append(view.render().el);
},
*/
});
});

96
bundle/dom_components/view/ComponentsView.js

@ -0,0 +1,96 @@
define(['backbone','require'],
function(Backbone, require) {
/**
* @class ComponentsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.config = o.config;
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
},
/**
* Add to collection
* @param {Object} Model
*
* @return void
* */
addTo: function(model){
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
},
/**
* Add new object to collection
* @param {Object} Model
* @param {Object} Fragment collection
* @param {Integer} Index of append
*
* @return {Object} Object rendered
* */
addToCollection: function(model, fragmentEl, index){
if(!this.compView)
this.compView = require('./ComponentView');
var fragment = fragmentEl || null,
viewObject = this.compView;
switch(model.get('type')){
case 'text':
if(!this.compViewText)
this.compViewText = require('./ComponentTextView');
viewObject = this.compViewText;
break;
case 'image':
if(!this.compViewImage)
this.compViewImage = require('./ComponentImageView');
viewObject = this.compViewImage;
break;
}
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
var p = this.$parent;
if(typeof index != 'undefined'){
var method = 'before';
// If the added model is the last of collection
// need to change the logic of append
if(p.children().length == index){
index--;
method = 'after';
}
// In case the added is new in the collection index will be -1
if(index < 0){
p.append(rendered);
}else
p.children().eq(index)[method](rendered);
}else{
p.append(rendered);
}
}
return rendered;
},
render: function($p) {
var fragment = document.createDocumentFragment();
this.$parent = $p || this.$el;
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
},this);
this.$el.append(fragment);
return this;
}
});
});

72
bundle/editor/config/config.js

@ -0,0 +1,72 @@
define(function () {
var config = {
// Style prefix
stylePrefix: 'wte-',
// Prefix to use inside local storage name
storagePrefix: 'wte-',
// Editor ID. Useful in case of multiple editors on the same page
id: '',
appContainer : '#wte-app',
idCanvas : 'canvas',
idCanvasOverlay : 'canvas-overlay',
idWrapper : 'wrapper',
// Enable/Disable undo manager
undoManager : true,
// Enable/Disable autosaving
autosave : true,
//Indicates which storage to use. Available: local | remote | none
storageType : 'local',
// If autosave enabled, indicates how many changes (general changes to structure)
// need to be done before save. Useful with remoteStorage to reduce remote calls
changesBeforeSave: 1,
//Enabled only if localStorage is false
remoteStorage : {},
//Configurations for Asset Manager
assetManager : {},
//Configurations for Canvas
canvas : {},
//Configurations for Style Manager
styleManager : {},
//Configurations for Layers
layers : {},
//Configurations for Storage Manager
storageManager : {},
//Configurations for Rich Text Editor
rte : {},
//Configurations for Components
components : {},
//Configurations for Modal Dialog
modal : {},
//Configurations for Code Manager
codeManager : {},
//Configurations for Panels
panels : {},
//Configurations for Commands
commands : {},
};
return config;
});

39
bundle/editor/main.js

@ -0,0 +1,39 @@
define(function (require){
/**
* @class Grapes
* @param {Object} Configurations
*
* @return {Object}
* */
var Grapes = function(config)
{
var c = config || {},
defaults = require('./config/config'),
Editor = require('./model/Editor'),
EditorView = require('./view/EditorView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.editor = new Editor(c);
var obj = {
model : this.editor,
config : c,
};
this.editorView = new EditorView(obj);
};
Grapes.prototype = {
render : function()
{
return this.editorView.render().$el;
}
};
return Grapes;
});

336
bundle/editor/model/Editor.js

@ -0,0 +1,336 @@
define([
'backbone',
'backboneUndo',
'keymaster',
'AssetManager',
'StorageManager',
'ModalDialog',
'CodeManager',
'Commands',
'Canvas',
'RichTextEditor',
'DomComponents',
'Panel'],
function(
Backbone,
UndoManager,
Keymaster,
AssetManager,
StorageManager,
ModalDialog,
CodeManager,
Commands,
Canvas,
RichTextEditor,
DomComponents,
Panels
){
return Backbone.Model.extend({
defaults:{
selectedComponent: null,
changesCount: 0,
},
initialize: function(c)
{
this.config = c;
this.compName = this.config.storagePrefix + 'componentsTree' + this.config.id;
this.set('Config', c);
this.initStorage();
this.initModal();
this.initAssetManager();
this.initCodeManager();
this.initCommands();
this.initPanels();
this.initRichTextEditor();
this.initComponents();
this.initCanvas();
this.initUndoManager();
this.on('change:selectedComponent', this.componentSelected, this);
},
/**
* Initialize components
* */
initComponents: function()
{
var cfg = this.config.components,
comps = this.loadComponentsTree(),
cmpStylePfx = cfg.stylePrefix || 'comp-';
if(!comps){
comps = {
removable : false,
movable : false,
badgable : false,
attributes : { id: this.config.idWrapper },
style : { position: 'relative',},
components : cfg.components,
};
}
cfg.stylePrefix = this.config.stylePrefix + cmpStylePfx;
cfg.component = comps;
if(this.rte)
cfg.rte = this.rte;
if(this.modal)
cfg.modal = this.modal;
if(this.am)
cfg.am = this.am;
this.cmp = new DomComponents(cfg);
if(this.stm.isAutosave()){ // TODO Currently doesn't listen already created models
this.updateComponents( this.cmp.getComponent(), null, { avoidStore : 1 });
}
this.set('Components', this.cmp);
},
/**
* Initialize canvas
* */
initCanvas: function()
{
var cfg = this.config.canvas,
pfx = cfg.stylePrefix || 'cv-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
this.cv = new Canvas(this.config.canvas);
if(this.cmp)
this.cv.setWrapper(this.cmp);
this.set('Canvas', this.cv);
},
/**
* Initialize rich text editor
* */
initRichTextEditor: function()
{
var cfg = this.config.rte,
rteStylePfx = cfg.stylePrefix || 'rte-';
cfg.stylePrefix = this.config.stylePrefix + rteStylePfx;
this.rte = new RichTextEditor(cfg);
this.set('RichTextEditor', this.rte);
},
/**
* Initialize storage
* */
initStorage: function()
{
this.stm = new StorageManager(this.config.storageManager);
this.stm.loadDefaultProviders().setCurrentProvider(this.config.storageType);
this.set('StorageManager', this.stm);
},
/**
* Initialize asset manager
* */
initAssetManager: function()
{
var cfg = this.config.assetManager,
pfx = cfg.stylePrefix || 'am-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
if(this.stm)
cfg.stm = this.stm;
this.am = new AssetManager(cfg);
this.set('AssetManager', this.am);
},
/**
* Initialize modal
* */
initModal: function()
{
var cfg = this.config.modal,
pfx = cfg.stylePrefix || 'mdl-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
this.modal = new ModalDialog(cfg);
this.modal.render().appendTo('body');
this.set('Modal', this.modal);
},
/**
* Initialize Code Manager
* */
initCodeManager: function()
{
var cfg = this.config.codeManager,
pfx = cfg.stylePrefix || 'cm-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
this.cm = new CodeManager(cfg);
this.cm.loadDefaultGenerators().loadDefaultEditors();
this.set('CodeManager', this.cm);
},
/**
* Initialize Commands
* */
initCommands: function()
{
var cfg = this.config.commands,
pfx = cfg.stylePrefix || 'com-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.em = this;
cfg.canvasId = this.config.idCanvas;
cfg.wrapperId = this.config.idWrapper;
this.com = new Commands(cfg);
this.com.loadDefaultCommands();
this.set('Commands', this.com);
},
/**
* Initialize Panels
* */
initPanels: function()
{
var cfg = this.config.panels,
pfx = cfg.stylePrefix || 'pn-';
cfg.stylePrefix = this.config.stylePrefix + pfx;
cfg.em = this;
this.pn = new Panels(cfg);
this.pn.addPanel({ id: 'views-container'});
this.set('Panels', this.pn);
},
/**
* Initialize Undo manager
* */
initUndoManager: function(){
if(this.cmp && this.config.undoManager){
var backboneUndo = new Backbone.UndoManager({
register: [this.cmp.getComponent().get('components')],
track: true
});
key('⌘+z, ctrl+z', function(){
backboneUndo.undo();
});
key('⌘+shift+z, ctrl+shift+z', function(){
backboneUndo.redo();
});
Backbone.UndoManager.removeUndoType("change");
var beforeCache;
Backbone.UndoManager.addUndoType("change:style", {
"on": function (model, value, opt) {
if(!beforeCache)
beforeCache = model.toJSON();
if (opt && opt.avoidStore) {
return;
} else {
var obj = {
"object": model,
"before": beforeCache,
"after": model.toJSON()
};
beforeCache = null;
return obj;
}
},
"undo": function (model, bf, af, opt) {
model.set(bf);
},
"redo": function (model, bf, af, opt) {
model.set(af);
}
});
//TODO when, for example, undo delete cant redelete it, so need to
//recall 'remove command'
}
},
/**
* Triggered when components are updated
* */
componentsUpdated: function()
{
var updatedCount = this.get('changesCount') + 1;
this.set('changesCount', updatedCount);
if(this.stm.isAutosave() && updatedCount < this.stm.getChangesBeforeSave()){
return;
}
this.storeComponentsTree();
this.set('changesCount', 0 );
},
/**
* Callback on component selection
* @param {Object} Model
* @param {Mixed} New value
* @param {Object} Options
*
* */
componentSelected: function(model, val, options)
{
if(!this.get('selectedComponent'))
this.trigger('deselect-comp');
else
this.trigger('select-comp',[model,val,options]);
},
/**
* Load components from storage
*
* @return {Object}
* */
loadComponentsTree: function(){
var result = null;
try{
result = JSON.parse(this.stm.load(this.compName));
}catch(err){
console.warn("Error encountered while parsing JSON response");
}
return result;
},
/**
* Save components to storage
*
* @return void
* */
storeComponentsTree: function(){
var wrp = this.cmp.getComponent();
if(wrp && this.cm){
var res = this.cm.getCode(wrp, 'json');
console.log(res);
this.stm.store(this.compName, JSON.stringify(res));
}
},
/**
* Triggered when components are updated
* @param {Object} model
* @param {Mixed} val Value
* @param {Object} opt Options
*
* */
updateComponents: function(model, val, opt){
var comps = model.get('components'),
avSt = opt ? opt.avoidStore : 0;
// Call stopListening for not creating nested listenings
this.stopListening(comps, 'add', this.updateComponents);
this.stopListening(comps, 'remove', this.componentsUpdated);
this.listenTo(comps, 'add', this.updateComponents);
this.listenTo(comps, 'remove', this.componentsUpdated);
this.stopListening(model, 'change:style change:content', this.updateComponents);
this.listenTo(model, 'change:style change:content', this.updateComponents);
if(!avSt)
this.componentsUpdated();
},
});
});

33
bundle/editor/view/EditorView.js

@ -0,0 +1,33 @@
define(['backbone'],
function(Backbone){
/**
* @class EditorView
* */
return Backbone.View.extend({
initialize: function() {
this.cv = this.model.get('Canvas');
this.pn = this.model.get('Panels');
this.className = this.model.config.stylePrefix + 'editor';
},
render: function(){
this.$el.empty();
if(this.cv)
this.$el.append(this.cv.render());
if(this.pn)
this.$el.append(this.pn.render());
this.$el.attr('class', this.className);
$('body '+this.model.config.appContainer).html(this.$el);
if(this.pn)
this.pn.active();
return this;
}
});
});

157
bundle/main.js

@ -0,0 +1,157 @@
require(['bundle/config/require-config.js'], function() {
require(['editor/main'],function (Grapes){
return Grapes;
});
/*
require(['startup/main'], function (Grapes){
/*
Grapes.run({
storageType: 'local',
remoteStorage: {
urlStore : 'http://test.localhost/wte/index.php',
urlLoad : 'http://test.localhost/wte/read.php',
paramsStore : { type:'homeTemplate',},
paramsLoad : { type:'homeTemplate',},
},
assetManager: {
storageType : 'local',
storeOnChange : true,
storeAfterUpload : true,
},
styleManager : {}, //sectors: [{ name: 'MainConfig', properties:[] }]
defaultComponents: [ { style: {'width':'500px', 'height': '35px', 'margin':'0 auto',}, },
{ style: {'width':'500px', 'height': '35px', 'margin':'0 auto',}, },
{ style: {'width':'500px', 'height': '35px', 'margin':'0 auto',}, },
{ style: {'width':'400px', 'height': '300px', 'margin':'0 auto', 'padding':'5px'},
components: [{ style: {'width':'130px','height': '30px','float':'left'}},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '80px','float':'left'},},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '70px','float':'left'},},
{ style: {'width':'50px','height': '80px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'50px','height': '50px','float':'left'},},
{ style: {'width':'80px','height': '50px','float':'left'},},
{ style: {'width':'75px','height': '50px','clear':'both'}}]
},
{ style: {'width':'700px', 'height': '250px', 'margin':'0 auto'},
components: [{ style: {'width':'100px','height': '30px','float':'left'}},
{ style: {'width':'200px','height': '50px','float':'left'},},
{ style: {'width':'150px','height': '150px','float':'left'},}]
},
{ style: {'width':'500px', 'height': '150px', 'margin':'0 auto'}, }
/*
{
css:{'width':'100%', 'background-color':'#372828'},
components:[{
css:{'width':'90%','max-width':'980px', 'min-height':'70px', 'margin':'0 auto'},
components: [
{
css:{'width':'50%','float':'left'},
components: [
{ editable: true, contents: 'myLogo', css:{ 'color':'#fff','font-size':'30px','padding-top':'20px','font-family':'Helvetica','font-weight':'100'} }
],
},
{
css:{'width':'50%','min-height':'70px','float':'left','font-family':'Helvetica','color':'#fff',},
components: [{ contents: '{{ TOPMENU }}', css:{ 'padding-top':'35px','font-weight':'100','text-align':'right'} },],
},
{css:{'clear':'both'}},
],
},],
},
{
css:{'width':'100%', 'background-color':'#e54b4b', 'font-family':'Helvetica'},
components:[
{
css:{'width':'90%','max-width':'980px','height':'500px', 'margin':'0 auto','text-align':'center',},//,'padding-top':'75px'
components: [
{ contents:'YOUR HOSTING SOLUTION',editable:true,
css:{ 'color':'#ffffff','font-size':'40px','font-weight':'700','text-align':'center','padding-top':'75px'}
},
{ contents:'Powerful hardware for you business right now',editable:true,
css:{ 'color':'#ffffff','font-size':'20px','font-weight':'100','text-align':'center','margin-top':'10px'}
},
{ attributes: {src:'./images/media/red-server-icon.png'}, tagName: 'img', css:{ 'display':'inline', 'margin':'70px auto','margin-left':'auto','margin-right':'auto'},}
//{css:{'width':'50%','min-height':'50px','float':'left'}},
//{css:{'width':'50%','min-height':'50px','float':'left'}},
],
},
],
},
{
css:{'width':'100%','background-color':'#ffffff'},
components:[
{
css:{'width':'90%','max-width':'980px','height':'350px', 'margin':'0 auto','color':'#352828'},//,'padding-top':'75px'
components: [
{
css:{ 'margin-left':'1%','margin-right':'1%','width':'31.333%', 'float':'left','height':'100%','padding-left':'20px','padding-right':'20px'},
components: [
{
css:{ 'width':'100px','height':'100px','margin':'0 auto', 'background-color':'#352828','border-radius':'50px','margin-top':'75px','text-align':'center'},
components: [
{ attributes: {src:'./images/media/cloud-logo.png'}, tagName: 'img', css:{'padding-top':'15px'},}
],
},
{ editable:true, contents:'Cloud servers', css:{ 'font-size':'20px','text-align':'center','margin-top':'25px'}},
{ editable:true,
contents:'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.',
css:{ 'text-align':'center','margin-top':'20px'}},
],
},
{
css:{ 'margin-left':'1%','margin-right':'1%','width':'31.333%', 'float':'left','height':'100%','padding-left':'20px','padding-right':'20px'},
components: [
{
css:{ 'width':'100px','height':'100px','margin':'0 auto', 'background-color':'#352828','border-radius':'50px','margin-top':'75px','text-align':'center'},
components: [
{ attributes: {src:'./images/media/network-icon.png'}, tagName: 'img', css:{'width':'59px','padding-top':'21px'},}
],
},
{ editable:true, contents:'Big networks', css:{ 'font-size':'20px','text-align':'center','margin-top':'25px'}},
{ editable:true,
contents:'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.',
css:{ 'text-align':'center','margin-top':'20px'}},
],
},
{
css:{ 'margin-left':'1%','margin-right':'1%','width':'31.333%', 'float':'left','height':'100%','padding-left':'20px','padding-right':'20px'},
components: [
{
css:{ 'width':'100px','height':'100px','margin':'0 auto', 'background-color':'#352828','border-radius':'50px','margin-top':'75px','text-align':'center'},
components: [
{ attributes: {src:'./images/media/settings-icon.png'}, tagName: 'img', css:{'width':'59px','padding-top':'21px'},}
],
},
{ editable:true, contents:'High scalability', css:{ 'font-size':'20px','text-align':'center','margin-top':'25px'}},
{ editable:true,
contents:'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.',
css:{ 'text-align':'center','margin-top':'20px'}},
],
},
],
},
],
},**
],
});//Initialize the application
*
$(window).unload(function() {
console.log('unload test (save data before exit)');
});
window.onbeforeunload = function(e) {
console.log('onbeforeunload test');
//return 'Please press the Logout button to logout.';
};
});
*/
});

13
bundle/modal_dialog/config/config.js

@ -0,0 +1,13 @@
define(function () {
return {
stylePrefix : 'mdl-',
title : '',
content : '',
backdrop : true,
};
});

57
bundle/modal_dialog/main.js

@ -0,0 +1,57 @@
define(function(require) {
/**
* @class Modal
* @param {Object} Configurations
*
* @return {Object}
* */
function Modal(config)
{
var c = config || {},
defaults = require('./config/config'),
ModalM = require('./model/Modal'),
ModalView = require('./view/ModalView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.model = new ModalM(c);
var obj = {
model : this.model,
config : c,
};
this.modal = new ModalView(obj);
}
Modal.prototype = {
getModel : function(){
return this.model;
},
render : function(){
return this.modal.render().$el;
},
show : function(){
return this.modal.show();
},
hide : function(){
return this.modal.hide();
},
setTitle : function(v){
return this.modal.setTitle(v);
},
setContent : function(v){
return this.modal.setContent(v);
},
};
return Modal;
});

15
bundle/modal_dialog/model/Modal.js

@ -0,0 +1,15 @@
define(['backbone'],
function(Backbone) {
/**
* @class Modal
* */
return Backbone.Model.extend({
defaults: {
title : '',
content : '',
open : false,
}
});
});

11
bundle/modal_dialog/template/modal.html

@ -0,0 +1,11 @@
<div class="<%= pfx %>dialog">
<div class="<%= pfx %>header">
<div class="<%= pfx %>title"><%= title %></div>
<div class="<%= pfx %>btn-close">&Cross;</div>
</div>
<div class="<%= pfx %>content">
<div id="<%= pfx %>c"> <%= content %> </div>
<div style="clear:both"></div>
</div>
</div>
<div class="<%= pfx %>backlayer"></div>

110
bundle/modal_dialog/view/ModalView.js

@ -0,0 +1,110 @@
define(['backbone', 'text!./../template/modal.html'],
function (Backbone, modalTemplate) {
/**
* @class ModalView
* */
return Backbone.View.extend({
template: _.template(modalTemplate),
events : {},
initialize: function(o){
this.config = o.config || {};
this.pfx = this.config.stylePrefix;
this.listenTo( this.model, 'change:open', this.updateOpen);
this.listenTo( this.model, 'change:title', this.updateTitle);
this.listenTo( this.model, 'change:content',this.updateContent);
this.events['click .'+this.pfx+'btn-close'] = 'hide';
if(this.config.backdrop)
this.events['click .'+this.pfx+'backlayer'] = 'hide';
},
/**
* Update content
*
* @return void
* */
updateContent: function(){
if(!this.$content)
this.$content = this.$el.find('.'+this.pfx+'content #'+this.pfx+'c');
this.$content.html(this.model.get('content'));
},
/**
* Update title
*
* @return void
* */
updateTitle: function(){
if(!this.$title)
this.$title = this.$el.find('.'+this.pfx+'title');
this.$title.html(this.model.get('title'));
},
/**
* Update open
*
* @return void
* */
updateOpen: function(){
if(this.model.get('open'))
this.$el.show();
else
this.$el.hide();
},
/**
* Hide modal
*
* @return void
* */
hide: function(){
this.model.set('open', 0);
},
/**
* Show modal
*
* @return void
* */
show: function(){
this.model.set('open', 1);
},
/**
* Set title
* @param {String} v Title
*
* @return this
* */
setTitle: function(v){
this.model.set('title',v);
return this;
},
/**
* Set content
* @param {String} v Title
*
* @return this
* */
setContent: function(v){
this.model.set('content',v);
return this;
},
render : function(){
var obj = this.model.toJSON();
obj.pfx = this.pfx;
this.$el.html( this.template(obj) );
this.$el.attr('class', this.pfx + 'container');
this.updateOpen();
console.log('Modal rendered');
return this;
},
});
});

10
bundle/navigator/config/config.js

@ -0,0 +1,10 @@
define(function () {
return {
stylePrefix : 'nv-',
sortable : true,
hidable : true,
containerId : 'navigator',
itemClass : 'item',
itemsClass : 'items',
};
});

40
bundle/navigator/main.js

@ -0,0 +1,40 @@
define(function(require) {
/**
* @class Navigator
* @param {Object} Collection
* @param {Object} Configurations
* */
function Navigator(collection, c)
{
var config = c,
defaults = require('./config/config'),
ItemsView = require('./view/ItemsView');
// Set default options
for (var name in defaults) {
if (!(name in config))
config[name] = defaults[name];
}
var obj = {
collection : collection,
config : config,
};
// Check if sort is required
if(config.sortable){
var ItemSort = require('./view/ItemSort');
obj.sorter = new ItemSort({config : config});
}
this.ItemsView = new ItemsView(obj);
}
Navigator.prototype = {
render : function(){
return this.ItemsView.render().$el;
},
};
return Navigator;
});

15
bundle/navigator/template/item.html

@ -0,0 +1,15 @@
<% if (hidable) { %>
<i id="<%= prefix %>btn-eye" class="btn fa fa-eye <%= (visible ? '' : 'fa-eye-slash') %>"></i>
<% } %>
<div class="<%= prefix %>title <%= addClass %>">
<i id="<%= prefix %>caret" class="fa fa-chevron-right"></i>
<%= title %>
</div>
<div id="<%= prefix %>counter"><%= (count ? count : '') %></div>
<div id="<%= prefix %>move">
<i class="fa fa-arrows"></i>
</div>
<div class="<%= prefix %>children"></div>

330
bundle/navigator/view/ItemSort.js

@ -0,0 +1,330 @@
define(['backbone'],
function(Backbone) {
/**
* @class ItemSort
* */
return Backbone.View.extend({
initialize: function(o) {
_.bindAll(this,'startMove','onMove','endMove','rollback', 'itemLeft');
this.config = o.config || {};
this.pfx = o.config.stylePrefix;
this.itemClass = '.' + this.pfx + this.config.itemClass;
this.itemsClass = '.' + this.pfx + this.config.itemsClass;
this.setElement('.'+this.pfx+this.config.containerId);
},
/**
* Picking component to move
* @param {Object} Element view
* @param {Object} Event
*
* @return void
* */
startMove: function(eV, e){
this.moved = false;
this.eV = eV;
this.$sel = this.eV.$el;
this.$selParent = this.$sel.closest(this.itemsClass);
// In case the component selected is not movable
if( !eV.model.get('movable') )
return;
// Create placeholder if not exists
if(!this.$plh){
var pfx = this.pfx;
this.$plh = $('<div>', {id: pfx + 'placeholder'}).css({'pointer-events':'none'}).hide();
this.$plh.append( $('<div>', {id: pfx + "plh-int", class: pfx + 'insert'} ) );
if(!this.$el.length)
this.$el = $('.'+this.pfx+this.config.containerId);
this.$plh.appendTo(this.$el);
}
this.$plh.data('hide',1);
eV.freeze();
this.$el.on('mousemove',this.onMove);
$(document).on('mouseup',this.endMove);
$(document).on('keypress',this.rollback);
},
/**
* Get children dimensions
* @param {Object} Parent element
*
* @retun {Array}
* */
getChildrenDim: function(el){
var dim = [],
p = el || this.$targetEl.parent(),
oT = this.elT,
oL = this.elL,
ch = p.children('.' + this.pfx + this.config.itemClass);
ch.each(function(){
var $el = $(this),
$elO = $el.offset();
dim.push( [ $elO.top - oT, $elO.left - oL, $el.outerHeight(), $el.outerWidth(), true, this]);
});
return dim;
},
/**
* During move
* @param {Object} Event
*
* @return void
* */
onMove: function(e){
this.moved = true;
if(this.$plh.data('hide')){
this.$plh.show();
this.$plh.data('hide',0);
}
var eO = this.$el.offset();
this.elT = eO.top;
this.elL = eO.left;
this.rY = (e.pageY - this.elT) + this.$el.scrollTop();
this.rX = (e.pageX - this.elL) + this.$el.scrollLeft();
this.inspect(e);
this.updatePosition(this.rX, this.rY);
var actualPos = this.posIndex+':'+this.posMethod;
//If there is a significant changes with the pointer
if(!this.lastPos || (this.lastPos != actualPos)){
this.updatePlaceholderPos(this.posIndex, this.posMethod);
this.lastPos = this.posIndex+':'+this.posMethod;
}
//Working alternative for find taget element
//var $targetEl = this.$selParent.children('.'+this.pfx+this.config.itemClass).eq(this.aIndex);
},
/**
* Search where to put placeholder
* @param int X position of the mouse
* @param int Y position of the mouse
* @retun void
* */
updatePosition: function( posX, posY ){
this.posMethod = "before";
this.posIndex = 0;
var leftLimit = 0, xLimit = 0, dimRight = 0, yLimit = 0, xCenter = 0, yCenter = 0, dimDown = 0, dim = 0;
for(var i = 0; i < this.cDim.length; i++){ //Dim => t,l,h,w
dim = this.cDim[i];
dimDown = dim[0] + dim[2];
yCenter = dim[0] + (dim[2] / 2); //Horizontal center
xCenter = dim[1] + (dim[3] / 2); //Vertical center
dimRight = dim[1] + dim[3];
if( (xLimit && dim[1] > xLimit) || (yLimit && yCenter > yLimit) ||
(leftLimit && dimRight < leftLimit)) //No need with this one if over the limit
continue;
if(!dim[4]){ //If it's not inFlow (like float element)
if( posY < dimDown)
yLimit = dimDown;
if( posX < xCenter){ //If mouse lefter than center
xLimit = xCenter;
this.posMethod = "before";
}else{
leftLimit = xCenter;
this.posMethod = "after";
}
this.posIndex = i;
}else{
this.posIndex = this.aIndex = i;
if( posY < yCenter ){ //If mouse upper than center
this.posMethod = "before"; //Should place helper before
if(posY < dim[0])
this.aIndex = i - 1;
break; //No need to continue under inFlow element
}else
this.posMethod = "after";
}
}
},
/**
* Updates the position of the placeholder
* @param int Index of the nearest child
* @param str Before or after position
* @return void
* */
updatePlaceholderPos: function(index, method){
var marg = 0, t = 0, l = 0, w = 0, h = 0,
un = 'px',
margI = 5,
plh = this.$plh[0];
if( this.cDim[index] ){
var elDim = this.cDim[index];
//If it's like with 'float' style
if(!elDim[4]){
w = 'auto';
h = elDim[2] - (marg * 2) + un;
t = elDim[0] + marg;
l = (method == 'before') ? (elDim[1] - marg) : (elDim[1] + elDim[3] - marg);
}else{
//w = '100%';
w = elDim[3] + un;
//h = elDim[3] + un;
t = (method == 'before') ? (elDim[0] - marg) : (elDim[0] + elDim[2] - marg);
l = elDim[1];
}
}else{
if(this.$targetEl){
var trg = this.$targetEl[0],
$elO = this.$targetEl.offset();
t = $elO.top - this.elT + margI + 17;
l = $elO.left - this.elL + margI * 7;
w = (parseInt(trg.offsetWidth) - margI * 14) + un;
}
}
plh.style.top = t + un;
plh.style.left = l + un;
if(w)
plh.style.width = w;
if(h)
plh.style.height = h;
},
/**
* Leave item
* @param event
*
* @return void
* */
endMove: function(e){
this.$el.off('mousemove',this.onMove);
$(document).off('mouseup', this.endMove);
$(document).off('keypress', this.rollback);
this.eV.unfreeze();
this.$plh.hide();
if(this.moved)
this.move(this.$targetEl, this.$sel, this.posIndex, this.posMethod);
this.itemLeft();
},
/**
* Move component to new position
* @param {Object} Component to move
* @param {Object} Target component
* @param {Integer} Indicates the position inside the collection
* @param {String} Before of after component
*
* @return void
* */
move: function(target, el, posIndex, method){
var trg = target|| this.$targetEl;
trg = trg || this.$backupEl;
if(!trg)
return;
var index = posIndex || 0;
var model = el.data("model");
var collection = model.collection;
var targetModel = trg.data('model');
var targetCollection = targetModel.collection;
if(!this.cDim.length)
targetCollection = targetModel.get('components');
if(targetCollection && targetModel.get('droppable')){
index = method == 'after' ? index + 1 : index;
var modelTemp = targetCollection.add({style:{}}, { at: index});
var modelRemoved = collection.remove(model, { silent:false });
targetCollection.add(modelRemoved, { at: index, silent:false });
targetCollection.remove(modelTemp);
}else
console.warn("Invalid target position");
},
/**
* Track inside which element pointer entered
* @param event
*
* @return void
* */
inspect: function(e){
var item = $(e.target).closest(this.itemClass);
if(!this.$targetEl || (item.length && item[0] != this.$targetEl[0]) ){
this.status = 1;
if(item.length){
this.$targetEl = this.$backupEl = item;
this.$targetElP = this.$targetEl.parent();
this.$targetsEl = this.$targetEl.find(this.itemsClass + ':first');
this.$targetEl.on('mouseleave', this.itemLeft);
this.targetM = this.$targetEl.data('model');
this.dimT = this.getTargetDim(this.$targetEl[0]);
this.cDim = this.getChildrenDim();
}
}else if( this.nearToBorders(this.$targetEl[0]) || this.$targetEl[0] == this.$sel[0] ){
if(this.status == 1){
this.status = 2;
this.lastPos = null;
this.cDim = this.getChildrenDim(this.$targetElP);
}
}else if( !this.nearToBorders(this.$targetEl[0]) ){
if(this.status == 2){
this.status = 1;
this.lastPos = null;
}
this.cDim = [];
}
},
/**
* Triggered when pointer leaves item
* @param event
*
* @return void
* */
itemLeft: function(e){
if(this.$targetEl){
this.$targetEl.off('mouseleave',this.itemLeft);
this.$targetEl = null;
}
},
/**
* Returns dimension of the target
* @param Event
*
* @return Array
* */
getTargetDim: function(e){
var $el = $(e),
$elO = $el.offset();
return [ $elO.top - this.elT, $elO.left - this.elL, $el.outerHeight(), $el.outerWidth() ];
},
/**
* Check if pointer is near to the borders of the target
* @param event
* @return Bool
* */
nearToBorders: function(e){
var m = 10; //Limit in pixels for be near
if(!this.dimT)
return;
var dimT = this.dimT;
if( ((dimT[0] + m) > this.rY) || (this.rY > (dimT[0] + dimT[2] - m)) ||
((dimT[1] + m) > this.rX) || (this.rX > (dimT[1] + dimT[3] - m)) )
return 1;
else
return 0;
},
/**
* Rollback to previous situation
* @param Event
* @param Bool Indicates if rollback in anycase
* @return void
* */
rollback: function(e, force){
var key = e.which || e.keyCode;
if(key == 27 || force){
this.moved = false;
this.endMove();
}
return;
},
});
});

198
bundle/navigator/view/ItemView.js

@ -0,0 +1,198 @@
define(['backbone', 'text!./../template/item.html','require'],
function (Backbone, ItemTemplate, require) {
/**
* @class ItemView
* */
return Backbone.View.extend({
template: _.template(ItemTemplate),
initialize: function(o){
this.opt = o;
this.config = o.config;
this.sorter = o.sorter || {};
this.pfx = this.config.stylePrefix;
if(typeof this.model.get('open') == 'undefined')
this.model.set('open',false);
this.listenTo(this.model.components, 'remove add change reset', this.checkChildren);
this.listenTo(this.model, 'destroy remove', this.remove);
//this.listenTo(this.model, 'change:status', this.updateStatus);
this.listenTo(this.model, 'change:open', this.updateOpening);
this.className = this.pfx + 'item no-select';
this.events = {};
this.events['click > #'+this.pfx+'btn-eye'] = 'toggleVisibility';
this.events['click .'+this.pfx+'title'] = 'toggleOpening';
this.$el.data("model", this.model);
if(o.config.sortable)
this.events['mousedown > #'+this.pfx+'move'] = 'startSort';
},
/**
* Update item opening
*
* @return void
* */
updateOpening: function (){
if(this.model.get('open')){
this.$el.addClass("open");
this.$caret.addClass('fa-chevron-down');
}else{
this.$el.removeClass("open");
this.$caret.removeClass('fa-chevron-down');
}
},
/**
* Toggle item opening
* @param {Object} e
*
* @return void
* */
toggleOpening: function(e){
e.stopPropagation();
if(!this.model.components.length)
return;
this.model.set('open', !this.model.get('open') );
},
/**
* Delegate to sorter
* @param Event
* */
startSort: function(e){
if(this.sorter)
this.sorter.startMove(this, e);
},
/**
* Freeze item
* @return void
* */
freeze: function(){
this.$el.addClass(this.pfx + 'opac50');
this.model.set('open',0);
},
/**
* Unfreeze item
* @return void
* */
unfreeze: function(){
this.$el.removeClass(this.pfx + 'opac50');
},
/**
* Update item on status change
* @param Event
*
* @return void
* */
updateStatus: function(e){
var s = this.model.get('status'),
pr = this.model.get('previousModel'),
pfx = this.pfx;
switch(s) {
case 'selected':
this.$el.addClass(pfx + 'selected');
break;
case 'moving':
break;
default:
this.$el.removeClass(pfx + 'selected');
}
if(pr){
pr.set('previousModel','');
pr.set('status','');
}
},
/**
* Toggle visibility
* @param Event
*
* @return void
* */
toggleVisibility: function(e){
if(!this.$eye)
this.$eye = this.$el.find('> #'+this.pfx+'btn-eye');
var cCss = _.clone(this.model.get('style')),
hClass = this.pfx + 'hide';
if(this.isVisible()){
this.$el.addClass(hClass);
this.$eye.addClass('fa-eye-slash');
cCss.display = 'none';
}else{
this.$el.removeClass(hClass);
this.$eye.removeClass('fa-eye-slash');
delete cCss.display;
}
this.model.set('style', cCss);
},
/**
* Check if component is visible
*
* @return bool
* */
isVisible: function(){
var css = this.model.get('style'),
pr = css.display;
if(pr && pr == 'none' )
return;
return 1;
},
/**
* Update item aspect after children changes
*
* @return void
* */
checkChildren: function(){
var c = this.model.components.length,
pfx = this.pfx;
if(!this.$counter)
this.$counter = this.$el.find('> #' + pfx + 'counter');
if(c){
this.$el.find('> .' + pfx + 'title').removeClass(pfx + 'no-chld');
this.$counter.html(c);
}else{
this.$el.find('> .' + pfx + 'title').addClass(pfx + 'no-chld');
this.$counter.empty();
this.model.set('open',0);
}
},
render : function(){
var pfx = this.pfx,
vis = this.isVisible();
this.$el.html( this.template({
title : this.model.getName(),
addClass : (this.model.components.length ? '' : pfx+'no-chld'),
count : this.model.components.length,
visible : vis,
hidable : this.config.hidable,
prefix : pfx
}));
if(typeof ItemsView == 'undefined')
ItemsView = require('./ItemsView');
this.$components = new ItemsView({
collection : this.model.components,
config : this.config,
sorter : this.sorter,
parent : this.model
}).render().$el;
this.$el.find('.'+ pfx +'children').html(this.$components);
this.$caret = this.$el.find('> .' + pfx + 'title > #' + pfx + 'caret');
if(!this.model.get('movable') || !this.config.sortable){
this.$el.find('> #' + pfx + 'move').detach();
}
if(!vis)
this.className += ' ' + pfx + 'hide';
this.$el.attr('class', _.result(this, 'className'));
return this;
},
});
});

89
bundle/navigator/view/ItemsView.js

@ -0,0 +1,89 @@
define(['backbone','./ItemView'],
function (Backbone, ItemView) {
/**
* @class ItemsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.opt = o;
this.config = o.config;
this.preview = o.preview;
this.sorter = o.sorter || {};
this.pfx = o.config.stylePrefix;
this.parent = o.parent;
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
this.className = this.pfx + 'items';
if(!this.parent)
this.className += ' ' + this.pfx + this.config.containerId;
},
/**
* Add to collection
* @param Object Model
*
* @return Object
* */
addTo: function(model){
var i = this.collection.indexOf(model);
this.addToCollection(model, null, i);
},
/**
* Add new object to collection
* @param Object Model
* @param Object Fragment collection
* @param integer Index of append
*
* @return Object Object created
* */
addToCollection: function(model, fragmentEl, index){
var fragment = fragmentEl || null;
var viewObject = ItemView;
var view = new viewObject({
model : model,
config : this.config,
sorter : this.sorter,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
if(typeof index != 'undefined'){
var method = 'before';
// If the added model is the last of collection
// need to change the logic of append
if(this.$el.children().length == index){
index--;
method = 'after';
}
// In case the added is new in the collection index will be -1
if(index < 0){
this.$el.append(rendered);
}else
this.$el.children().eq(index)[method](rendered);
}else
this.$el.append(rendered);
}
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
},this);
this.$el.append(fragment);
this.$el.attr('class', _.result(this, 'className'));
return this;
}
});
});

48
bundle/panel/config/config.js

@ -0,0 +1,48 @@
define(function () {
return {
stylePrefix : 'pn-',
defaults : [{
id : 'commands',
buttons : [{ id: 'select', className: 'fa fa-mouse-pointer', command: 'select-comp', attributes: {title:'Create'}},
{ id: 'create', className: 'fa fa-plus-square-o', command: 'create-comp',
buttons: [
{ id: 'image2', className: 'fa fa-picture-o', command: 'image-comp' },
{ id: 'move2', className: 'fa fa-arrows', command: 'move-comp' },
{ id: 'text2', className: 'fa fa-font' , command: 'text-comp' },
{ id: 'var', className: 'fa fa-hashtag', command: 'insert-var',
options: { content: '{{ VAR22 }}', terminateAfterInsert: false, }, },
] },
{ id: 'remove', className: 'fa fa-minus-square-o', command: 'delete-comp' },
{ id: 'move', className: 'fa fa-arrows', command: 'move-comp' },
{ id: 'resize', className: 'fa fa-arrows-alt', command: 'resize-comp' },
{ id: 'text', className: 'fa fa-font' , command: 'text-comp' },
{ id: 'image', className: 'fa fa-picture-o', command: 'image-comp' },
{ id: 'var', className: 'fa fa-hashtag', command: 'insert-var',
options: { content: '{{ VAR11 }}', terminateAfterInsert: true, },
buttons: [
{ id: 'image2', className: 'fa fa-picture-o', command: 'image-comp' },
{ id: 'move2', className: 'fa fa-arrows', command: 'move-comp' },
{ id: 'text2', className: 'fa fa-font' , command: 'text-comp' },
{ id: 'var', className: 'fa fa-hashtag', command: 'insert-var',
options: { content: '{{ VAR22 }}', terminateAfterInsert: false, }, },
]},
],
},{
id : 'options',
buttons : [{ id: 'visibility', className: 'fa fa-eye', command: 'sw-visibility', active: true, context: 'sw-visibility' },
//{ id: 'select2', className: 'fa fa-mouse-pointer', command: 'select-comp' },
{ id: 'export', className: 'fa fa-code', command: 'export-template' },],
},{
id : 'views',
buttons : [{ id: 'open-sm', className: 'fa fa-paint-brush', command: 'open-sm'},
{ id: 'open-layers', className: 'fa fa-bars', command: 'open-layers' },],
}],
// Editor model
em : null,
// Delay before show children buttons (in milliseconds)
delayBtnsShow : 300,
};
});

74
bundle/panel/main.js

@ -0,0 +1,74 @@
define(function(require) {
/**
* @class Panel
* @param {Object} Configurations
*
* @return {Object}
* */
function Panel(config)
{
var c = config || {},
defaults = require('./config/config'),
Panels = require('./model/Panels'),
PanelsView = require('./view/PanelsView');
// Set default options
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.panels = new Panels(c.defaults);
var obj = {
collection : this.panels,
config : c,
};
this.PanelsView = new PanelsView(obj);
}
Panel.prototype = {
getPanels : function(){
return this.panels;
},
addPanel : function(obj){
return this.panels.add(obj);
},
getPanel : function(id){
var res = this.panels.where({id: id});
return res.length ? res[0] : null;
},
addButton : function(panelId, obj){
var pn = this.getPanel(panelId);
return pn ? pn.get('buttons').add(obj) : null;
},
getButton : function(panelId, id){
var pn = this.getPanel(panelId);
if(pn){
var res = pn.get('buttons').where({id: id});
return res.length ? res[0] : null;
}
return null;
},
active : function(){
this.getPanels().each(function(p){
p.get('buttons').each(function(btn){
if(btn.get('active'))
btn.trigger('updateActive');
});
});
},
render : function(){
return this.PanelsView.render().el;
},
};
return Panel;
});

26
bundle/panel/model/Button.js

@ -0,0 +1,26 @@
define([ 'backbone','require'],
function (Backbone, require) {
/**
* @class Button
* */
return Backbone.Model.extend({
defaults :{
id : '',
className : '',
command : '',
context : '',
buttons : [],
attributes : {},
active : false,
},
initialize: function(options) {
if(this.get('buttons').length){
var Buttons = require('./Buttons');
this.set('buttons', new Buttons(this.get('buttons')) );
}
},
});
});

44
bundle/panel/model/Buttons.js

@ -0,0 +1,44 @@
define([ 'backbone','./Button'],
function (Backbone, Button) {
/**
* @class Buttons
* */
return Backbone.Collection.extend({
model: Button,
/**
* Deactivate all buttons, except one passed
* @param {Object} except Model to ignore
* @param {Boolean} r Recursive flag
*
* @return void
* */
deactivateAllExceptOne: function(except, r){
this.forEach(function(model, index) {
if(model !== except){
model.set('active', false);
if(r && model.get('buttons').length)
model.get('buttons').deactivateAllExceptOne(except,r);
}
});
},
/**
* Deactivate all buttons
* @param {String} context Context string
*
* @return void
* */
deactivateAll: function(context){
this.forEach(function(model, index) {
if( model.get('context') == context ){
model.set('active', false);
if(model.get('buttons').length)
model.get('buttons').deactivateAll(context);
}
});
},
});
});

22
bundle/panel/model/Panel.js

@ -0,0 +1,22 @@
define([ 'backbone','./Buttons'],
function (Backbone, Buttons) {
/**
* @class Panel
* */
return Backbone.Model.extend({
defaults :{
id : '',
content : '',
visible : true,
buttons : [],
},
initialize: function(options) {
this.btn = this.get('buttons') || [];
this.buttons = new Buttons(this.btn);
this.set('buttons', this.buttons);
},
});
});

11
bundle/panel/model/Panels.js

@ -0,0 +1,11 @@
define([ 'backbone','./Panel'],
function (Backbone, Panel) {
/**
* @class Panels
* */
return Backbone.Collection.extend({
model: Panel,
});
});

236
bundle/panel/view/ButtonView.js

@ -0,0 +1,236 @@
define(['backbone','require'],
function(Backbone, require) {
/**
* @class ButtonView
* */
return Backbone.View.extend({
tagName : 'span',
events : { 'click' : 'clicked' },
initialize: function(o){
_.bindAll(this, 'startTimer', 'stopTimer', 'showButtons', 'hideButtons','closeOnKeyPress');
this.config = o.config;
this.em = this.config.em || {};
this.pfx = this.config.stylePrefix;
this.id = this.pfx + this.model.get('id');
this.className = this.pfx + 'btn ' + this.model.get('className');
this.activeCls = this.pfx + 'active';
this.btnsVisCls = this.pfx + 'visible';
this.parentM = o.parentM || null;
this.listenTo(this.model, 'change:active updateActive', this.updateActive);
this.listenTo(this.model, 'checkActive', this.checkActive);
this.listenTo(this.model, 'change:bntsVis', this.updateBtnsVis);
this.listenTo(this.model, 'change:attributes', this.updateAttributes);
this.listenTo(this.model, 'change:className', this.updateClassName);
if(this.model.get('buttons').length){
this.$el.disableSelection();
this.$el.on('mousedown', this.startTimer);
this.$el.append($('<div>',{class: this.pfx + 'arrow-rd'}));
}
if(this.em)
this.commands = this.em.get('Commands');
},
/**
* Updates class name of the button
*
* @return void
* */
updateClassName: function()
{
this.$el.attr('class', this.pfx + 'btn ' + this.model.get('className'));
},
/**
* Updates attributes of the button
*
* @return void
* */
updateAttributes: function()
{
this.$el.attr(this.model.get("attributes"));
},
/**
* Updates visibility of children buttons
*
* @return void
* */
updateBtnsVis: function()
{
if(!this.$buttons)
return;
if(this.model.get('bntsVis'))
this.$buttons.addClass(this.btnsVisCls);
else
this.$buttons.removeClass(this.btnsVisCls);
},
/**
* Start timer for showing children buttons
*
* @return void
* */
startTimer: function()
{
this.timeout = setTimeout(this.showButtons, this.config.delayBtnsShow);
$(document).on('mouseup', this.stopTimer);
},
/**
* Stop timer for showing children buttons
*
* @return void
* */
stopTimer: function()
{
$(document).off('mouseup', this.stopTimer);
if(this.timeout)
clearTimeout(this.timeout);
},
/**
* Show children buttons
*
* @return void
* */
showButtons: function()
{
clearTimeout(this.timeout);
this.model.set('bntsVis', true);
$(document).on('mousedown', this.hideButtons);
$(document).on('keypress', this.closeOnKeyPress);
},
/**
* Hide children buttons
*
* @return void
* */
hideButtons: function(e)
{
if(e){ $(e.target).trigger('click'); }
this.model.set('bntsVis', false);
$(document).off('mousedown', this.hideButtons);
$(document).off('keypress', this.closeOnKeyPress);
},
/**
* Close buttons on ESC key press
* @param {Object} e Event
*
* @return void
* */
closeOnKeyPress: function(e)
{
var key = e.which || e.keyCode;
if(key == 27)
this.hideButtons();
},
/**
* Update active status of the button
*
* @return void
* */
updateActive: function(){
var command = null;
if(this.commands)
command = this.commands.get(this.model.get('command'));
if(this.model.get('active')){
//this.$el.addClass(this.activeCls);
//this.model.collection.deactivateAllExceptOne(this.model);
this.model.collection.deactivateAll(this.model.get('context'));
this.model.set('active', true, { silent: true }).trigger('checkActive');
if(this.parentM)
this.parentM.set('active', true, { silent: true }).trigger('checkActive');
if(command)
command.run(this.em, this.model);
}else{
this.$el.removeClass(this.activeCls);
this.model.collection.deactivateAll(this.model.get('context'));
if(this.parentM)
this.parentM.set('active', false, { silent: true }).trigger('checkActive');
if(command)
command.stop(this.em, this.model);
}
},
/**
* Update active style status
*
* @return void
* */
checkActive: function(){
if(this.model.get('active'))
this.$el.addClass(this.activeCls);
else
this.$el.removeClass(this.activeCls);
},
/**
* Triggered when button is clicked
* @param {Object} e Event
*
* @return void
* */
clicked: function(e)
{
if(this.model.get('bntsVis') )
return;
if(this.parentM)
this.swapParent();
this.model.set('active', !this.model.get('active'));
},
/**
* Updates parent model swapping properties
*
* @return void
* */
swapParent: function()
{
this.parentM.collection.deactivateAll(this.model.get('context'));
this.parentM.set('attributes', this.model.get('attributes'));
this.parentM.set('options', this.model.get('options'));
this.parentM.set('command', this.model.get('command'));
this.parentM.set('className', this.model.get('className'));
this.parentM.set('active', true, { silent: true }).trigger('checkActive');
},
render: function()
{
this.updateAttributes();
this.$el.attr('class', this.className);
if(this.model.get('buttons').length){
var btnsView = require('./ButtonsView'); //Avoid Circular Dependencies
var view = new btnsView({
collection : this.model.get('buttons'),
config : this.config,
parentM : this.model
});
this.$buttons = view.render().$el;
this.$buttons.append($('<div>',{class: this.pfx + 'arrow-l'}));
this.$el.append(this.$buttons); //childNodes avoids wrapping 'div'
}
return this;
},
});
});

68
bundle/panel/view/ButtonsView.js

@ -0,0 +1,68 @@
define(['backbone','./ButtonView'],
function (Backbone, ButtonView) {
/**
* @class ButtonsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.opt = o;
this.config = o.config;
this.pfx = o.config.stylePrefix;
this.parentM = o.parentM || null;
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
this.className = this.pfx + 'buttons';
},
/**
* Add to collection
* @param Object Model
*
* @return Object
* */
addTo: function(model){
this.addToCollection(model);
},
/**
* Add new object to collection
* @param Object Model
* @param Object Fragment collection
*
* @return Object Object created
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = ButtonView;
var view = new viewObject({
model : model,
config : this.config,
parentM : this.parentM
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
this.$el.append(rendered);
}
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
}, this);
this.$el.append(fragment);
this.$el.attr('class', _.result(this, 'className'));
return this;
}
});
});

50
bundle/panel/view/PanelView.js

@ -0,0 +1,50 @@
define(['backbone','./ButtonsView'],
function(Backbone, ButtonsView) {
/**
* @class PanelView
* */
return Backbone.View.extend({
initialize: function(o){
this.config = o.config;
this.pfx = this.config.stylePrefix;
this.buttons = this.model.get('buttons');
this.className = this.pfx + 'panel';
this.id = this.pfx + this.model.get('id');
this.listenTo(this.model, 'change:appendContent', this.appendContent);
this.listenTo(this.model, 'change:content', this.updateContent);
},
/**
* Append content of the panel
* */
appendContent: function()
{
this.$el.append(this.model.get('appendContent'));
},
/**
* Update content
* */
updateContent: function()
{
this.$el.html(this.model.get('content'));
},
render: function() {
this.$el.attr('class', _.result(this, 'className'));
this.$el.attr('id', this.id);
if(this.buttons.length){
var buttons = new ButtonsView({
collection : this.buttons,
config : this.config,
});
this.$el.append(buttons.render().el);
}
this.$el.append(this.model.get('content'));
return this;
},
});
});

67
bundle/panel/view/PanelsView.js

@ -0,0 +1,67 @@
define(['backbone','./PanelView'],
function (Backbone, PanelView) {
/**
* @class ItemsView
* */
return Backbone.View.extend({
initialize: function(o) {
this.opt = o;
this.config = o.config;
this.pfx = o.config.stylePrefix;
this.listenTo( this.collection, 'add', this.addTo );
this.listenTo( this.collection, 'reset', this.render );
this.className = this.pfx + 'panels';
},
/**
* Add to collection
* @param Object Model
*
* @return Object
* */
addTo: function(model){
this.addToCollection(model);
},
/**
* Add new object to collection
* @param Object Model
* @param Object Fragment collection
* @param integer Index of append
*
* @return Object Object created
* */
addToCollection: function(model, fragmentEl){
var fragment = fragmentEl || null;
var viewObject = PanelView;
var view = new viewObject({
model : model,
config : this.config,
});
var rendered = view.render().el;
if(fragment){
fragment.appendChild(rendered);
}else{
this.$el.append(rendered);
}
return rendered;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(model){
this.addToCollection(model, fragment);
}, this);
this.$el.append(fragment);
this.$el.attr('class', _.result(this, 'className'));
return this;
}
});
});

23
bundle/rich_text_editor/config/config.js

@ -0,0 +1,23 @@
define(function () {
return {
stylePrefix : 'rte-',
toolbarId : 'toolbar',
containerId : 'wrapper',
commands : [{
command: 'bold',
title: 'Bold',
class: 'fa fa-bold',
group: 'format'
},{
command: 'italic',
title: 'Italic',
class: 'fa fa-italic',
group: 'format'
},{
command: 'underline',
title: 'Underline',
class: 'fa fa-underline',
group: 'format'
},],
};
});

131
bundle/rich_text_editor/main.js

@ -0,0 +1,131 @@
define(function(require) {
/**
* @class RichTextEditor
* @param {Object} Configurations
*
* @return {Object}
* */
function RichTextEditor(config)
{
var c = config || {},
rte = require('rte'),
defaults = require('./config/config'),
CommandButtons = require('./model/CommandButtons'),
CommandButtonsView = require('./view/CommandButtonsView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.tlbPfx = c.stylePrefix;
this.commands = new CommandButtons(c.commands);
var obj = {
collection : this.commands,
config : c,
};
this.toolbar = new CommandButtonsView(obj);
this.$toolbar = this.toolbar.render().$el;
}
RichTextEditor.prototype = {
/**
* Bind rich text editor to element
* @param {Object} view
* @param {Object} container
*
* @return void
* */
bind : function(view, container){
if(!this.$contaniner){
this.$container = container;
this.$toolbar.appendTo(this.$container);
}
view.$el.wysiwyg({hotKeys: {}}).focus();
this.updatePosition(view.$el);
this.bindToolbar(view).show();
//Avoid closing edit mode clicking on toolbar
this.$toolbar.on('mousedown', this.disableProp);
},
/**
* Unbind rich text editor from element
* @param {Object} view
*
* @return void
* */
unbind : function(view){
view.$el.wysiwyg('destroy');
this.hide();
this.$toolbar.off('mousedown', this.disableProp);
},
/**
* Bind toolbar to element
* @param {Object} view
*
* @return this
* */
bindToolbar : function(view){
var id = this.tlbPfx + view.model.cid,
dId = this.tlbPfx + 'inited';
if(!view.$el.data(dId)){
view.$el.data(dId, 1);
view.$el.attr('id',id);
}
this.toolbar.updateTarget('#' + id);
return this;
},
/**
* Update toolbar position
* @param {Object} $el Element
*
* @return void
*/
updatePosition: function($el){
var cOffset = this.$container.offset(),
cTop = cOffset ? cOffset.top : 0,
cLeft = cOffset ? cOffset.left : 0,
eOffset = $el.offset(),
rTop = eOffset.top - cTop + this.$container.scrollTop(),
rLeft = eOffset.left - cLeft + this.$container.scrollLeft();
if(!this.tlbH)
this.tlbH = this.$toolbar.outerHeight();
this.$toolbar.css({
top : (rTop - this.tlbH - 5),
left : rLeft
});
},
/**
* Show toolbar
*
* @return void
* */
show : function(){
this.$toolbar.show();
},
/**
* Hide toolbar
*
* @return void
* */
hide : function(){
this.$toolbar.hide();
},
/**
* Isolate disable propagation method
* @param Event
* */
disableProp: function(e){
e.stopPropagation();
},
};
return RichTextEditor;
});

16
bundle/rich_text_editor/model/CommandButton.js

@ -0,0 +1,16 @@
define(['backbone'],
function (Backbone) {
/**
* @class CommandButton
* */
return Backbone.Model.extend({
defaults: {
command : '',
title : '',
class : '',
group : '',
},
});
});

11
bundle/rich_text_editor/model/CommandButtons.js

@ -0,0 +1,11 @@
define([ 'backbone','./CommandButton'],
function (Backbone, CommandButton) {
/**
* @class CommandButtons
* */
return Backbone.Collection.extend({
model: CommandButton,
});
});

20
bundle/rich_text_editor/view/CommandButtonView.js

@ -0,0 +1,20 @@
define(['backbone'],
function (Backbone) {
/**
* @class CommandButtonView
* */
return Backbone.View.extend({
tagName: 'a',
initialize: function(o){
this.config = o.config || {};
this.className = this.config.stylePrefix + 'btn ' + this.model.get('class');
},
render: function() {
this.$el.attr('class', _.result( this, 'className' ) );
return this;
}
});
});

51
bundle/rich_text_editor/view/CommandButtonsView.js

@ -0,0 +1,51 @@
define(['backbone','./CommandButtonView'],
function (Backbone, CommandButtonView) {
/**
* @class CommandButtonsView
* */
return Backbone.View.extend({
className: 'no-dots',
attributes : {
'data-role' : 'editor-toolbar',
},
initialize: function(o){
this.config = o.config || {};
this.id = this.config.stylePrefix + this.config.toolbarId;
this.$el.data('helper',1);
},
/**
* Update RTE target pointer
* @param {String} target
*
* @return this
* */
updateTarget: function(target){
this.$el.attr('data-target',target);
return this;
},
render: function() {
var fragment = document.createDocumentFragment();
this.$el.empty();
this.collection.each(function(item){
var view = new CommandButtonView({
model : item,
config : this.config,
attributes : {
'title' : item.get('title'),
'data-edit' : item.get('command'),
},
});
fragment.appendChild(view.render().el);
},this);
this.$el.append(fragment);
this.$el.attr('id', _.result( this, 'id' ) );
return this;
}
});
});

51
bundle/storage_manager/config/config.js

@ -0,0 +1,51 @@
define(function () {
return {
// Enable/Disable autosaving
autosave : 1,
// Indicates which storage to use. Available: local | remote
storageType : 'local',
// If autosave enabled, indicates how many changes (general changes to structure)
changesBeforeSave : 1,
// Defaults for remote storage
remoteStorage : {
//Enable/Disable components model (JSON format)
storeComponents: true,
//Enable/Disable styles model (JSON format)
storeStyles: false,
//Enable/Disable saving HTML template
storeHTML: false,
/**
* Url where to save all stuff.
* The request will send a POST via AJAX, like this:
* {
* components: '',
* style: '',
* html: '', //if storeHTML is enabled
* }
* */
urlStore: '',
/**
* Use this url to fetch model data, does expect in response something like this:
* { data: {
* components: '',
* style: '',
* } }
*/
urlLoad: '',
/**
* Url where assets will be send
* */
urlUpload: '',
paramsStore :{}, //Custom parameters to pass with set request
paramsLoad :{}, //Custom parameters to pass with get request
beforeSend : function(jqXHR,settings){}, //Callback before request
onComplete : function(jqXHR,status){}, //Callback after request
},
// Defaults for local storage
localStorage : {},
};
});

184
bundle/storage_manager/main.js

@ -0,0 +1,184 @@
define(function(require) {
/**
* @class StorageManager
* @param {Object} Configurations
*
* @return {Object}
* */
function StorageManager(config)
{
var c = config || {},
defaults = require('./config/config'),
LocalStorage = require('./model/LocalStorage'),
RemoteStorage = require('./model/RemoteStorage'),
StorageInterface = require('./model/StorageInterface');
for (var name in defaults){
if (!(name in c))
c[name] = defaults[name];
}
this.providers = {};
this.defaultProviders = {};
this.autosave = c.autosave;
this.currentProvider = c.storageType || null;
this.changesBeforeSave = c.changesBeforeSave;
this.si = new StorageInterface();
var Local = new LocalStorage(c.localStorage),
Remote = new RemoteStorage(c.remoteStorage);
this.defaultProviders[Local.getId()] = Local;
this.defaultProviders[Remote.getId()] = Remote;
}
StorageManager.prototype = {
/**
* Check if autosave enabled
*
* @return boolean
* */
isAutosave : function(){
return this.autosave;
},
/**
* Set autosave
* @param {Mixed} v Value
*
* @return this
* */
setAutosave : function(v){
this.autosave = v;
return this;
},
/**
* Returns value of changes required before save
*
* @return {Integer}
* */
getChangesBeforeSave : function(){
return this.changesBeforeSave;
},
/**
* Set changesBeforeSave value
* @param {Mixed} v Value
*
* @return this
* */
setChangesBeforeSave : function(v){
this.changesBeforeSave = v;
return this;
},
/**
* Add new storage provider
* @param {StorageInterface} provider
*
* @return self
* */
addProvider : function(provider) {
// Check interface implementation
for (var method in this.si)
if(!provider[method])
console.warn("addProvider: method '"+ method +"' was not found inside '"+ provider.getId() +"' object");
this.providers[provider.getId()] = provider;
if(!this.currentProvider)
this.currentProvider = provider.getId();
return this;
},
/**
* Returns storage provider
* @param {Integer} id Storage provider ID
*
* @return {StorageInterface}|null
* */
getProvider : function(id){
var provider = null;
if(id && this.providers[id])
provider = this.providers[id];
return provider;
},
/**
* Returns storage providers
*
* @return {Array}
* */
getProviders : function(){
return this.providers;
},
/**
* Get current provider
*
* @return {StorageInterface}
* */
getCurrentProvider : function() {
if(!this.currentProvider)
this.loadDefaultProviders();
return this.getProvider(this.currentProvider);
},
/**
* Set current provider
* @param {Integer} id Storage provider ID
*
* @return this
* */
setCurrentProvider : function(id) {
this.currentProvider = id;
return this;
},
/**
* Load default providers
*
* @return this
* */
loadDefaultProviders : function() {
for (var id in this.defaultProviders) {
this.addProvider(this.defaultProviders[id]);
}
return this;
},
/**
* Store resource
* @param {String} name Name of the resource
* @param {String} value Value of the resource
*
* @return {Object}|void
* */
store : function(name, value){
return this.getCurrentProvider().store(name, value);
},
/**
* Load resource
* @param {String} name Name of the resource
*
* @return {Object}|void
* */
load : function(name){
return this.getCurrentProvider().load(name);
},
/**
* Remove resource
* @param {String} name Name of the resource
*
* @return {Object}|void
* */
remove : function(name){
return this.getCurrentProvider().remove(name);
},
};
return StorageManager;
});

59
bundle/storage_manager/model/LocalStorage.js

@ -0,0 +1,59 @@
define(['backbone'],
function (Backbone) {
/**
* @class LocalStorage
* */
return Backbone.Model.extend({
id: 'local',
defaults: {
checkSupport : true,
errorNoSupport : 'Error encountered while parsing JSON response',
},
/** @inheritdoc */
getId : function() {
return this.id;
},
/** @inheritdoc */
store : function(name, value) {
this.checkStorageEnvironment();
localStorage.setItem(name, value );
},
/** @inheritdoc */
load: function(name){
var result = null;
this.checkStorageEnvironment();
if(localStorage.getItem(name))
result = localStorage.getItem(name);
try{
var prx = "Loading '" + name + "': ";
if(!result)
throw prx + ' Resource was not found';
}catch(err){
console.warn(err);
}
return result;
},
/** @inheritdoc */
remove : function(name) {
this.checkStorageEnvironment();
localStorage.removeItem(name);
},
/**
* Check storage environment
* @return void
* */
checkStorageEnvironment: function(){
if(this.get('checkSupport'))
if( !localStorage )
console.warn(this.get('errorNoSupport'));
},
});
});

80
bundle/storage_manager/model/RemoteStorage.js

@ -0,0 +1,80 @@
define(['backbone'],
function (Backbone) {
/**
* @class RemoteStorage
* */
return Backbone.Model.extend({
id: 'remote',
defaults: {
urlLoad : 'http://localhost/load',
urlStore : 'http://localhost/store',
beforeSend : function(){},
onComplete : function(){},
paramsStore : {},
paramsLoad : {},
errorLoad : 'Response is not a valid JSON',
},
/** @inheritdoc */
getId : function() {
return this.id;
},
/** @inheritdoc */
store : function(name, value) {
var fd = new FormData(),
params = this.get('paramsStore');
fd.append( name, value );
for(var key in params){
fd.append(key, params[key] );
}
$.ajax({
url: this.get('urlStore'),
beforeSend: this.get('beforeSend'),
complete: this.get('onComplete'),
type: 'POST',
processData: false,
contentType: false,
data: fd,
});
},
/** @inheritdoc */
load: function(name){
var result = null,
t = this;
$.ajax({
url : this.get('urlLoad'),
beforeSend : this.get('beforeSend'),
complete : this.get('onComplete'),
data : this.get('paramsLoad'),
async : false,
type : 'GET',
}).done(function(d){
try{
var prx = "Loading '" + name + "': ";
if(typeof d !== 'object')
throw prx + t.get('errorLoad');
result = d.data ? d.data[name] : d[name];
if(!result)
throw prx + ' Resource was not found';
}catch(err){
console.warn(err);
}
});
return result;
},
/** @inheritdoc */
remove : function(name) {
},
});
});

19
bundle/storage_manager/model/StorageInterface.js

@ -0,0 +1,19 @@
define(function() {
/**
* @class StorageInterface
* */
function StorageInterface() {}
StorageInterface.prototype = {
getId : function() {},
store : function(name, value) {},
load : function(name) {},
remove : function(name) {},
};
return StorageInterface;
});

431
bundle/style_manager/config/config.js

@ -0,0 +1,431 @@
define(function () {
return {
stylePrefix : 'sm-',
target : null,
//sectors: [],
sectors: [{
name: 'Positions',
properties:[{
name: 'Alignment',
property: 'float',
type: 'radio',
defaults : 'none',
list: [{
value : 'none',
//icon: 'none',
info: 'None',
},{
value : 'left',
info: 'Float element to the left',
//icon: 'float-left',
},{
value : '0 auto',
//icon: 'float-center',
info: 'Center the element',
property: 'margin'
},{
value : 'right',
info: 'Float element to the right',
//icon: 'float-right',
}],
}],
},{
name: 'Dimension',
properties:[{
name: 'Width',
property: 'width',
type: 'integer',
units: ['px','%'],
defaults : 'auto',
min: 0,
},{
name: 'Height',
property: 'height',
type: 'integer',
units: ['px','%'],
defaults : 'auto',
min: 0,
},],
},{
name: 'Typography',
properties:[{
name: 'Font',
property: 'font-family',
type: 'select',
defaults : 'Arial, Helvetica, sans-serif',
list: [{
value : 'Arial, Helvetica, sans-serif',
name : 'Arial',
style: 'font-family: Arial, Helvetica, sans-serif',
},{
value : '"Arial Black", Gadget, sans-serif',
style: 'font-family: "Arial Black", Gadget, sans-serif',
name : 'Arial Black',
},{
value : '"Brush Script MT", sans-serif',
style: 'font-family: "Brush Script MT", sans-serif',
name : 'Brush Script MT',
},{
value : '"Comic Sans MS", cursive, sans-serif',
style: 'font-family: "Comic Sans MS", cursive, sans-serif',
name : 'Comica Sans',
},{
value : '"Courier New", Courier, monospace',
style: 'font-family: "Courier New", Courier, monospace',
name : 'Courier New',
},{
value : 'Georgia, serif',
style: 'font-family: Georgia, serif',
name : 'Georgia',
},{
value : 'Helvetica, serif',
style: 'font-family: Helvetica, serif',
name : 'Helvetica',
},{
value : 'Impact, Charcoal, sans-serif',
style: 'font-family: Impact, Charcoal, sans-serif',
name : 'Impact',
},{
value : '"Lucida Sans Unicode", "Lucida Grande", sans-serif',
style: 'font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif',
name : 'Lucida Sans',
},{
value : 'Tahoma, Geneva, sans-serif',
style: 'font-family: Tahoma, Geneva, sans-serif',
name : 'Tahoma',
},{
value : '"Times New Roman", Times, serif',
style: 'font-family: "Times New Roman", Times, serif',
name : 'Times New Roman',
},{
value : '"Trebuchet MS", Helvetica, sans-serif',
style: 'font-family: "Trebuchet MS", Helvetica, sans-serif',
name : 'Trebuchet',
},{
value : 'Verdana, Geneva, sans-serif',
style: 'font-family: Verdana, Geneva, sans-serif',
name : 'Verdana',
}],
},{
name: 'Weight',
property: 'font-weight',
type: 'select',
defaults : '400',
list: [{ value : '100', name : 'Thin', },
{ value : '200', name : 'Extra-Light', },
{ value : '300', name : 'Light', },
{ value : '400', name : 'Normal', },
{ value : '500', name : 'Medium',},
{ value : '600', name : 'Semi-Bold',},
{ value : '700', name : 'Bold', },
{ value : '800', name : 'Extra-Bold',},
{ value : '900', name : 'Ultra-Bold', }],
},{
name: 'Text align',
property: 'text-align',
type: 'radio',
defaults : 'left',
list: [{ value : 'left', name : 'Left', style:"font-weight:bold", },
{ value : 'center', name : 'Center', },
{ value : 'right', name : 'Right', },
{ value : 'justify', name : 'Justify', },],
},{
name: 'Weight',
property: 'font-weight',
type: 'select',
defaults : '400',
list: [{ value : '100', name : 'Thin', },
{ value : '200', name : 'Extra-Light', },
{ value : '300', name : 'Light', },
{ value : '400', name : 'Normal', },
{ value : '500', name : 'Medium',},
{ value : '600', name : 'Semi-Bold',},
{ value : '700', name : 'Bold', },
{ value : '800', name : 'Extra-Bold',},
{ value : '900', name : 'Ultra-Bold', }],
}],
},{
name: 'Decorations',
properties: [{
name: 'Borders radius',
property: 'border-radius',
type: 'integer',
units: ['px'],
unit: 'px',
defaults : '0',
min: 0,
},{
name: 'Borders radius',
property: 'border-radius',
type: 'composite',
properties:[{
name: 'Top',
property: 'pad-top',
type: 'integer',
units: ['px','%'],
defaults : 0,
min: 0,
},{
name: 'Right',
property: 'pad-right',
type: 'integer',
units: ['px','%'],
min: 0,
defaults : 0,
},{
name: 'Bottom',
property: 'pad-bot',
type: 'integer',
units: ['px','%'],
min: 0,
defaults : 0,
},{
name: 'Left',
property: 'pad-left',
type: 'integer',
units: ['px'],
min: 0,
defaults : 0,
},],
},
{
name: 'Box shadow',
property: 'box-shadow',
type: 'stack',
preview: true,
properties:[{
name: 'Shadow type',
property: 'shadow-type',
type: 'select',
defaults: '',
list: [ { value : '', name : 'Outside', },
{ value : 'inset', name : 'Inside', }],
},{
name: 'X position',
property: 'shadow-x',
type: 'integer',
units: ['px','%'],
defaults : 0,
},{
name: 'Y position',
property: 'shadow-y',
type: 'integer',
units: ['px','%'],
defaults : 0,
},{
name: 'Blur',
property: 'shadow-blur',
type: 'integer',
units: ['px'],
defaults : 5,
min: 0,
},{
name: 'Spread',
property: 'shadow-spread',
type: 'integer',
units: ['px'],
defaults : 0,
},{
name: 'Color',
property: 'shadow-color',
type: 'color',
defaults: 'black',
},
],
},{
name: 'Background',
property: 'background',
type: 'stack',
preview: true,
properties:[{
name: 'Image',
property: 'background-image',
type: 'file',
defaults: 'none',
},
/*{
name: 'Background position',
property: 'background-position',
type: 'composite',
properties: [ {
name: 'BpX',
property: 'bpx',
type: 'select',
defaults: 'left',
list: [ { value : 'left', name : 'Left', },
{ value : 'center', name : 'Center', },
{ value : 'right', name : 'Right', }],
},{
name: 'BpY',
property: 'bpy',
type: 'select',
defaults: 'top',
list: [ { value : 'top', name : 'Top', },
{ value : 'center', name : 'Center', },
{ value : 'bottom', name : 'Bottom', }],
},
],
}*/
{
name: 'Repeat',
property: 'background-repeat',
type: 'select',
defaults: 'repeat',
list: [{ value : 'repeat', name : 'Repeat', },
{ value : 'repeat-x', name : 'Repeat X', },
{ value : 'repeat-y', name : 'Repeat Y', },
{ value : 'no-repeat', name : 'No repeat', }],
},
{
name: 'Position X',
property: 'bpx',
type: 'select',
defaults: 'left',
list: [ { value : 'left', name : 'Left', },
{ value : 'center', name : 'Center', },
{ value : 'right', name : 'Right', }],
},{
name: 'Position Y',
property: 'bpy',
type: 'select',
defaults: 'top',
list: [ { value : 'top', name : 'Top', },
{ value : 'center', name : 'Center', },
{ value : 'bottom', name : 'Bottom', }],
},{
name: 'Attach',
property: 'background-attachment',
type: 'select',
defaults: 'scroll',
list: [{ value : 'scroll', name : 'Scroll', },
{ value : 'fixed', name : 'Fixed', },
{ value : 'local', name : 'Local', }],
},
/*{
name: 'Background origin',
property: 'background-origin',
type: 'select',
defaults: 'padding-box',
list: [{ value : 'padding-box', name : 'Padding', },
{ value : 'border-box', name : 'Border', },
{ value : 'content-box', name : 'Content', }],
},{
name: 'Background clip',
property: 'background-clip',
type: 'select',
defaults: 'border-box',
list: [{ value : 'border-box', name : 'Border', },
{ value : 'padding-box', name : 'Padding', },
{ value : 'content-box', name : 'Content', }],
},{
name: 'Color',
property: 'background-color',
type: 'color',
defaults: 'black',
},*/
],
},{
name: 'Transition',
property: 'transition',
type: 'stack',
preview: false,
properties:[{
name: 'Property',
property: 'transition-property',
type: 'select',
defaults: '',
list: [{ value : 'width', name : 'Width', },
{ value : 'height', name : 'Height', },
{ value : 'background-color', name : 'Background', }],
},{
name: 'Duration',
property: 'transition-duration',
type: 'integer',
units: ['s'],
defaults : '2',
min: 0,
},{
name: 'Easing',
property: 'transition-timing-function',
type: 'select',
defaults: 'ease',
list: [{ value : 'linear', name : 'Linear', },
{ value : 'ease', name : 'Ease', },
{ value : 'ease-in', name : 'Ease-in', },
{ value : 'ease-out', name : 'Ease-out', },
{ value : 'ease-in-out', name : 'Ease-in-out', }],
}],
},{
name: 'Perspective',
property: 'perspective',
type: 'integer',
units: ['px'],
defaults : '0',
min: 0,
},{
name: 'Transform',
property: 'transform',
type: 'composite',
properties:[{
name: 'Rotate X',
property: 'transform-rotate-x',
type: 'integer',
units: ['deg'],
defaults : '0',
functionName: 'rotateX',
},{
name: 'Rotate Y',
property: 'transform-rotate-y',
type: 'integer',
units: ['deg'],
defaults : '0',
functionName: 'rotateY',
},{
name: 'Rotate Z',
property: 'transform-rotate-z',
type: 'integer',
units: ['deg'],
defaults : '0',
functionName: 'rotateZ',
}],
}/*{
name: 'Padding',
property: 'padding',
type: 'composite',
properties:[{
name: 'Top',
property: 'pad-top',
type: 'integer',
units: ['px','%'],
defaults : 0,
min: 0,
},{
name: 'Right',
property: 'pad-right',
type: 'integer',
units: ['px','%'],
min: 0,
defaults : 0,
},{
name: 'Bottom',
property: 'pad-bot',
type: 'integer',
units: ['px','%'],
min: 0,
defaults : 0,
},{
name: 'Left',
property: 'pad-left',
type: 'integer',
units: ['px'],
min: 0,
defaults : 0,
},],
},*/
],
}
],
};
});

80
bundle/style_manager/main.js

@ -0,0 +1,80 @@
define(function(require) {
/**
* @class StyleManager
* @param {Object} Configurations
*
* @return {Object}
* */
function StyleManager(config)
{
var c = config || {},
defaults = require('./config/config'),
Sectors = require('./model/Sectors'),
SectorsView = require('./view/SectorsView');
for (var name in defaults) {
if (!(name in c))
c[name] = defaults[name];
}
this.sectors = new Sectors(c.sectors);
var obj = {
collection : this.sectors,
target : c.target,
config : c,
};
this.SectorsView = new SectorsView(obj);
}
StyleManager.prototype = {
/**
* Get all sectors
*
* @return {Sectors}
* */
getSectors : function()
{
return this.sectors;
},
/**
* Get sector by id
* @param {String} id Object id
*
* @return {Sector}|{null}
* */
getSector : function(id)
{
var res = this.sectors.where({id: id});
return res.length ? res[0] : null;
},
/**
* Add new Sector
* @param {String} id Object id
* @param {Object} obj Object data
*
* @return {Sector}
* */
addSector : function(id, obj)
{
if(!this.getSector(id)){
obj.id = id;
return this.sectors.add(obj);
}
},
/**
* Render sectors
*
* @return {String}
* */
render : function(){
return this.SectorsView.render().$el;
},
};
return StyleManager;
});

16
bundle/style_manager/model/Layer.js

@ -0,0 +1,16 @@
define(['backbone'],
function(Backbone) {
/**
* @class Layer
* */
return Backbone.Model.extend({
defaults: {
name : '',
active : true,
value : '',
preview : false,
}
});
});

11
bundle/style_manager/model/Layers.js

@ -0,0 +1,11 @@
define([ 'backbone','./Layer'],
function (Backbone,Layer) {
/**
* @class Layers
* */
return Backbone.Collection.extend({
model: Layer,
});
});

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save