├── .gitignore ├── BUILD.md ├── CHANGES.md ├── LICENSE ├── README.md ├── bower.json ├── gulpfile.js ├── package.json ├── preview.png └── src ├── app ├── app.controller.js ├── app.js ├── app.routes.js ├── directives │ ├── dragnode.directive.js │ ├── dropnode.directive.js │ ├── keytable.directive.js │ ├── keytable.html │ ├── tab.directive.js │ ├── tab.html │ ├── tabset.directive.js │ └── tabset.html ├── models │ ├── project.model.js │ └── settings.model.js ├── pages │ ├── dash │ │ ├── dash.controller.js │ │ └── dash.html │ ├── editor │ │ ├── components │ │ │ ├── menubar.controller.js │ │ │ ├── menubar.html │ │ │ ├── nodespanel.controller.js │ │ │ ├── nodespanel.html │ │ │ ├── propertiespanel.controller.js │ │ │ ├── propertiespanel.html │ │ │ ├── treespanel.controller.js │ │ │ └── treespanel.html │ │ ├── editor.controller.js │ │ ├── editor.html │ │ └── modals │ │ │ ├── editnode.controller.js │ │ │ ├── editnode.html │ │ │ ├── export.controller.js │ │ │ ├── export.html │ │ │ ├── import.controller.js │ │ │ ├── import.html │ │ │ └── modal.html │ ├── home │ │ ├── home.controller.js │ │ └── home.html │ ├── projects │ │ ├── projects.controller.js │ │ └── projects.html │ └── settings │ │ ├── settings.controller.js │ │ └── settings.html ├── services │ ├── dialog.service.js │ ├── editor.service.js │ ├── nodejs.service.js │ ├── notification.service.js │ ├── storage │ │ ├── filestorage.service.js │ │ ├── localstorage.service.js │ │ └── storage.service.js │ └── system.service.js └── validators │ └── blacklist.directive.js ├── assets ├── css │ └── preload.css ├── imgs │ ├── closedhand.cur │ └── favicon.ico ├── js │ └── preload.js ├── less │ ├── animations.less │ ├── bootstrap.less │ ├── c_app.less │ ├── c_dash.less │ ├── c_keytable.less │ ├── c_menubar.less │ ├── c_modal.less │ ├── c_nodes.less │ ├── c_notification.less │ ├── c_page.less │ ├── c_properties.less │ ├── c_sidebar.less │ ├── c_tabset.less │ ├── c_trees.less │ ├── index.less │ └── variables.less └── libs │ ├── behavior3js-0.1.0.min.js │ ├── createjs.min.js │ ├── creatine-1.0.0.min.js │ └── mousetrap.min.js ├── desktop.js ├── editor ├── draw │ ├── shapes.js │ └── symbols.js ├── editor │ ├── Editor.js │ ├── managers │ │ ├── ExportManager.js │ │ ├── ImportManager.js │ │ ├── ProjectManager.js │ │ └── ShortcutManager.js │ └── systems │ │ ├── CameraSystem.js │ │ ├── CollapseSystem.js │ │ ├── ConnectionSystem.js │ │ ├── DragSystem.js │ │ ├── SelectionSystem.js │ │ └── ShortcutSystem.js ├── namespaces.js ├── project │ ├── Project.js │ └── managers │ │ ├── HistoryManager.js │ │ ├── NodeManager.js │ │ └── TreeManager.js ├── tree │ ├── Tree.js │ └── managers │ │ ├── BlockManager.js │ │ ├── ConnectionManager.js │ │ ├── EditManager.js │ │ ├── OrganizeManager.js │ │ ├── SelectionManager.js │ │ └── ViewManager.js └── utils │ ├── Block.js │ ├── Command.js │ ├── Connection.js │ ├── EditorError.js │ ├── Node.js │ ├── Root.js │ ├── SelectionBox.js │ ├── SettingsManager.js │ ├── functions.js │ └── settings.js ├── index.html ├── package.json └── start.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | # IDE configurations 31 | .idea 32 | 33 | # Custom 34 | bower_components 35 | dist 36 | .temp-dist 37 | build 38 | src/vendor 39 | cache 40 | teste.py 41 | 42 | ignore-* 43 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | # Building Behavior3 Editor 2 | 3 | You can build the editor in two different environments: for development and for production. For development you can run a local web server that will build and reload automatically the application for each new modification on the project. The production mode builds and package the editor for different platforms. 4 | 5 | 6 | ## Requirements 7 | 8 | To run the editor you will need the following softwares: 9 | 10 | **required for everything:** 11 | - [NodeJS](https://nodejs.org) 12 | - [Bower](http://bower.io) 13 | 14 | *if you want to run/build the desktop version:* 15 | - [Node-Webkit](http://nwjs.io) 16 | - [Node-Webkit Builder](https://github.com/nwjs/nw-builder) 17 | 18 | 19 | ## Configuration 20 | 21 | Before building, you need to install some 3rd-party libraries. You need to run in console the following commands: 22 | 23 | npm install 24 | 25 | and: 26 | 27 | bower install 28 | 29 | The former installs a bunch of NodeJS modules, which are used on the building system and some dependences of the desktop application. The last installs CSS and Javascript vendor libraries. 30 | 31 | 32 | ## Building during development 33 | 34 | During development you can run the editor in a web browser with automatically building and reloading: 35 | 36 | gulp serve 37 | 38 | which will run a web server hosted on `http://127.0.0.1:8000`. 39 | 40 | To run the desktop version (without automatically building and reload): 41 | 42 | gulp nw 43 | 44 | 45 | ## Building final version 46 | 47 | Just run: 48 | 49 | gulp dist 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | 2 | ## Version 0.3.0 [NEXT] 3 | 4 | - mod: services splitted into models and services. 5 | - mod: new building system and dependence organization. 6 | - mod: version and building date automatically inserted into code. 7 | - mod: minor presentation changes on loading screen and dash home. 8 | - mod: desktop version now uses electron (see #14). 9 | - mod: desktop version working on windows and linux (see #14). 10 | - fix: cut now remove all nodes, and consider connections too (see #9). 11 | - fix: ctrl+z does not affect property inputs anymore (see #10). 12 | - fix: preview not showing in the first drag on Firefox (see #16). 13 | 14 | 15 | ## Version 0.2.0 [Not released] 16 | 17 | **editor**: 18 | 19 | - add: editor import and export custom_node property (see #9/b3js). 20 | - add: editor create custom nodes automatically when importing (see #9/b3js). 21 | - add: interface is using AngularJS now. 22 | - add: editor now support multiple trees, you can change it on tree panel. 23 | - add: history manager (undo/redo feature) (#13). 24 | - add: desktop version using nwjs. 25 | - add: a settings panels (see #6). 26 | - add: node categories can have different colors (see #4). 27 | - add: alt-click to select subtree (see #10). 28 | - add: optional vertical layout (see #5). 29 | - add: editor now can hanble multiple projects (see #15). 30 | - mod: new commands and shortcuts (see #16, #17, #10 and #13). 31 | - mod: new repository. 32 | - mod: new architecture, merging viewer and editor (see #1). 33 | - mod: further updates to the architecture. 34 | - mod: parameters are read-only now, so they cannot be edited (see #2). 35 | - mod: all communication (editor->app) is done by events. 36 | - fix: error when importing json without Display. 37 | - rem: dependence on JQuery and other libraries is removed. 38 | 39 | 40 | ## Version 0.1.0 [Out 27, 2014] 41 | 42 | - initial release. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Renato de Pontes Pereira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BEHAVIOR3EDITOR 2 | 3 | ![interface preview](preview.png) 4 | 5 | **Behavior3 Editor** is the official visual editor for the **Behavior3** libraries. It can be accessed online or you can download it to have handle local projects. 6 | 7 | 8 | ## Why Behavior3 Editor? 9 | 10 | Why should you use b3editor? What is different from other editors? Can it compete against commercial alternatives? - Well, check it out some characteristics of Behavior3 Editor: 11 | 12 | - **Open Source Software**: under MIT license, you can use this software freely, adapt it to your need and even use a specialized internal version in your company. You can also contribute with bug fixes, suggestions and patches to make it better. 13 | 14 | - **Open Format**: b3editor can export the modeled trees to JSON files, following an open format. If there is no official reader on your favorite language yet, you can develop your own library and use the trees created here. 15 | 16 | - **Formality**: the editor works above the basis created by Behavior3JS, which in turn is based on formal description of behavior trees. Thus, the editor provides a stable solution to model agents for your games or other applications such as robotics and simulations in general. 17 | 18 | - **Focus on Usability**: intuitiveness is the key word of b3editor. We focus on providing an easy, clean, and intuitive tool for programmers and non-programmers. If there is something obscure or too difficult to use, report it immediately! 19 | 20 | - **Minimalist, but Functional**: b3editor follows a minimalist style, trying to reduce the amount of non-essential information presented on the screen. We focus on the important things: designing Behavior Trees. 21 | 22 | - **Customizable**: create your own node types and customize nodes instances individually. Create several projects and trees, change titles and add properties. 23 | 24 | - **Big Projects Ahead**: we are working towards a collaborative tool in order to provide an awesome editor for big projects involving several designers working together. 25 | 26 | - **Does not depends on other tools/editors/engines**. 27 | 28 | 29 | 30 | ## Main features 31 | 32 | - **Custom Nodes**: you can create your own node types inside one of the four basic categories - *composite*, *decorator*, *action* or *condition*. 33 | - **Individual Node Properties**: you can modify node titles, description and custom properties. 34 | - **Manual and Auto Organization**: organize by dragging nodes around or just type "a" to auto organize the whole tree. 35 | - **Create and Manage Multiple Trees**: you can create and manage an unlimited number of trees. 36 | - **Import and Export to JSON**: export your project, tree or nodes to JSON format. Import them back. Use JSON on your own custom library or tool. You decide. 37 | 38 | 39 | ## Limitations 40 | 41 | Nothing is perfect =( . Behavior3 Editor focus on Chrome (thus, working pretty well on Opera too), so it have some incompatibilities with Firefox, such as the image preview lag when dragging to create a node for the first time, and the ugly scroll bar inside the panels. Not tested on IE! 42 | 43 | 44 | ## Looking for Behavior Tree Libraries? 45 | 46 | - https://github.com/behavior3/behavior3js 47 | - https://github.com/behavior3/behavior3py 48 | 49 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "behavior3editor", 3 | "version": "0.2.0dev", 4 | "directory": "src/vendor/", 5 | "devDependencies": { 6 | "angular" : "~1.4", 7 | "bootstrap" : "~3.1.1", 8 | "angular-animate" : "~1.4", 9 | "angular-bootstrap" : "~0.13.0", 10 | "angular-ui-router" : "~0.2.14", 11 | "fontawesome" : "~4.3.0", 12 | "less" : "~2.5.0", 13 | "sweetalert" : "~1.0.0-beta" 14 | }, 15 | "dependencies": {} 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Renato de Pontes Pereira", 3 | "name": "behavior3editor", 4 | "version": "0.3.0", 5 | "homepage": "http://behavior3.com", 6 | "license": "MIT", 7 | "bugs": "https://github.com/behavior3/behavior3editor/issues", 8 | "repository": "github:behavior3/behavior3editor", 9 | "devDependencies": { 10 | "electron-packager": "^5.1.1", 11 | "electron-prebuilt": "^0.33.8", 12 | "gulp": "~3.9.0", 13 | "gulp-angular-templatecache": "~1.7.0", 14 | "gulp-concat": "~2.6.0", 15 | "gulp-connect": "~2.2.0", 16 | "gulp-foreach": "^0.1.0", 17 | "gulp-jshint": "~1.11.2", 18 | "gulp-less": "~3.0.3", 19 | "gulp-minify-css": "~1.2.1", 20 | "gulp-minify-html": "~1.0.4", 21 | "gulp-replace": "~0.5.4", 22 | "gulp-uglify": "~1.4.1", 23 | "gulp-zip": "^3.0.2", 24 | "jshint-stylish": "~2.0.1", 25 | "merge-stream": "~1.0.0", 26 | "rimraf": "^2.4.3" 27 | }, 28 | "dependencies": {} 29 | } 30 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/behavior3/behavior3editor/20c2202c7ca9616dbbd5146f87f3f8df3d3567f5/preview.png -------------------------------------------------------------------------------- /src/app/app.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('AppController', AppController); 7 | 8 | AppController.$inject = [ 9 | '$scope', 10 | '$window', 11 | 'dialogService' 12 | ]; 13 | 14 | function AppController($scope, 15 | $window, 16 | dialogService) { 17 | 18 | // HEAD // 19 | var vm = this; 20 | 21 | _active(); 22 | 23 | // BODY // 24 | function _active() { 25 | window.onbeforeunload = _onBeforeCloseBrowser; 26 | 27 | try { 28 | var gui = require('nw.gui'); 29 | var win = gui.Window.get(); 30 | 31 | win.on('close', function() { 32 | _onBeforeCloseDesktop(win); 33 | }); 34 | } catch (e) {} 35 | } 36 | 37 | function _onBeforeCloseBrowser() { 38 | if ($window.editor.isDirty()) { 39 | return "Leaving now will erase your unsaved changes."; 40 | } 41 | } 42 | function _onBeforeCloseDesktop(win) { 43 | if ($window.editor.isDirty()) { 44 | dialogService 45 | .confirm( 46 | 'Leave without saving?', 47 | 'If you proceed you will lose all unsaved modifications.', 48 | null) 49 | .then(function() { 50 | win.close(true); 51 | }); 52 | return false; 53 | } else { 54 | win.close(true); 55 | } 56 | 57 | } 58 | } 59 | 60 | })(); -------------------------------------------------------------------------------- /src/app/app.js: -------------------------------------------------------------------------------- 1 | angular.module('app', [ 2 | 'ui.router', 3 | 'ui.bootstrap', 4 | 'ngAnimate', 5 | 'templates' 6 | ]) 7 | 8 | .run(['$rootScope', '$window', '$state', 9 | function Execute($rootScope, $window, $state) { 10 | $rootScope.isDesktop = !!$window.process && !!$window.require; 11 | 12 | $rootScope.go = function(state, params) { 13 | $state.go(state, params); 14 | }; 15 | } 16 | ]) 17 | 18 | .run(['$window', '$animate', '$location', '$document', '$timeout', 'settingsModel', 'projectModel', 19 | function Execute($window, 20 | $animate, 21 | $location, 22 | $document, 23 | $timeout, 24 | settingsModel, 25 | projectModel) { 26 | 27 | // reset path 28 | $location.path('/'); 29 | 30 | // add drop to canvas 31 | angular 32 | .element($window.editor._game.canvas) 33 | .attr('b3-drop-node', true); 34 | 35 | // initialize editor 36 | settingsModel.getSettings(); 37 | projectModel 38 | .getRecentProjects() 39 | .then(function(projects) { 40 | 41 | function closePreload() { 42 | $timeout(function() { 43 | var element = angular.element(document.getElementById('page-preload')); 44 | $animate.addClass(element, 'preload-fade') 45 | .then(function() { 46 | element.remove(); 47 | }); 48 | }, 500); 49 | } 50 | 51 | if (projects.length > 0 && projects[0].isOpen) { 52 | projectModel 53 | .openProject(projects[0].path) 54 | .then(function() { 55 | closePreload(); 56 | }); 57 | } else { 58 | closePreload(); 59 | } 60 | }); 61 | } 62 | ]); 63 | -------------------------------------------------------------------------------- /src/app/app.routes.js: -------------------------------------------------------------------------------- 1 | angular.module('app') 2 | 3 | .config(['$stateProvider', '$urlRouterProvider', 4 | function($stateProvider, $urlRouterProvider) { 5 | $urlRouterProvider.otherwise('/dash/home'); 6 | 7 | $stateProvider 8 | // Dash 9 | .state('dash', { 10 | url: '/dash', 11 | abstract: true, 12 | templateUrl: 'pages/dash/dash.html', 13 | controller: 'DashController', 14 | controllerAs: 'dash', 15 | }) 16 | .state('dash.home', { 17 | url: '/home', 18 | templateUrl: 'pages/home/home.html', 19 | controller: 'HomeController', 20 | controllerAs: 'home', 21 | }) 22 | .state('dash.projects', { 23 | url: "/projects", 24 | templateUrl: 'pages/projects/projects.html', 25 | controller: 'ProjectsController', 26 | controllerAs: 'projects', 27 | }) 28 | .state('dash.settings', { 29 | url: "/settings", 30 | templateUrl: 'pages/settings/settings.html', 31 | controller: 'SettingsController', 32 | controllerAs: 'settings', 33 | }) 34 | 35 | // Editor 36 | .state('editor', { 37 | url: "/editor", 38 | templateUrl: 'pages/editor/editor.html', 39 | controller: 'EditorController', 40 | controllerAs: 'editor', 41 | }) 42 | .state('editor.editnode', { 43 | url: "/node/:name", 44 | templateUrl: 'pages/editor/modals/editnode.html', 45 | controller: 'EditNodeController', 46 | controllerAs: 'editnode', 47 | }) 48 | .state('editor.export', { 49 | url: "/export/:type/:format", 50 | templateUrl: 'pages/editor/modals/export.html', 51 | controller: 'ExportController', 52 | controllerAs: 'export', 53 | }) 54 | .state('editor.import', { 55 | url: "/import/:type/:format", 56 | templateUrl: 'pages/editor/modals/import.html', 57 | controller: 'ImportController', 58 | controllerAs: 'import', 59 | }); 60 | } 61 | ]); -------------------------------------------------------------------------------- /src/app/directives/dragnode.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3DragNode', dragNode); 7 | 8 | dragNode.$inject = [ 9 | '$window' 10 | ]; 11 | 12 | function dragNode($window) { 13 | var directive = { 14 | restrict : 'A', 15 | link : link, 16 | }; 17 | return directive; 18 | 19 | function link(scope, element, attrs) { 20 | element.attr('draggable', 'true'); 21 | 22 | element.bind('dragstart', function(e) { 23 | var canvas = $window.editor.preview(attrs.name); 24 | var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome')>-1; 25 | 26 | if (isChrome) { 27 | var img = document.createElement('img'); 28 | img.src = canvas.toDataURL(); 29 | 30 | // 10ms delay in order to proper create the image object 31 | // ugly hack =( 32 | var time = (new Date()).getTime(); 33 | var delay = time + 10; 34 | while (time < delay) { 35 | time = (new Date()).getTime(); 36 | } 37 | canvas = img; 38 | } 39 | 40 | e.dataTransfer.setData('name', attrs.name); 41 | e.dataTransfer.setDragImage(canvas, canvas.width/2, canvas.height/2); 42 | }); 43 | } 44 | } 45 | 46 | })(); 47 | 48 | 49 | // .directive('draggableNode', function($window) { 50 | // return { 51 | // restrict: 'A', 52 | // link: function(scope, element, attributes, controller) { 53 | // angular.element(element).attr("draggable", "true"); 54 | // element.bind("dragstart", function(e) { 55 | // var img = $window.app.editor.preview(attributes.id.replace('node-', '')); 56 | 57 | // e.dataTransfer.setData('text', attributes.id); 58 | // e.dataTransfer.setDragImage(img, img.width/2, img.height/2); 59 | // }); 60 | // } 61 | // } 62 | // }) 63 | -------------------------------------------------------------------------------- /src/app/directives/dropnode.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3DropNode', dropNode); 7 | 8 | dropNode.$inject = [ 9 | '$window' 10 | ]; 11 | 12 | function dropNode($window) { 13 | var directive = { 14 | restrict : 'A', 15 | link : link, 16 | }; 17 | return directive; 18 | 19 | function link(scope, element, attrs) { 20 | element.bind('dragover', function(e) { 21 | if (e.preventDefault) { 22 | e.preventDefault(); 23 | } 24 | return false; 25 | }); 26 | element.bind('drop', function(e) { 27 | if (e.preventDefault) { 28 | e.preventDefault(); 29 | } 30 | if (e.stopPropagation) { 31 | e.stopPropagation(); 32 | } 33 | 34 | var name = e.dataTransfer.getData('name'); 35 | 36 | var project = $window.editor.project.get(); 37 | var tree = project.trees.getSelected(); 38 | var point = tree.view.getLocalPoint(e.clientX, e.clientY); 39 | tree.blocks.add(name, point.x, point.y); 40 | 41 | $window.editor._game.canvas.focus(); 42 | }); 43 | } 44 | } 45 | 46 | })(); 47 | -------------------------------------------------------------------------------- /src/app/directives/keytable.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3KeyTable', keytable) 7 | .controller('KeyTableController', KeyTableController); 8 | 9 | keytable.$inject = ['$parse']; 10 | function keytable($parse) { 11 | var directive = { 12 | require : '^ngModel', 13 | restrict : 'EA', 14 | replace : true, 15 | bindToController : true, 16 | controller : 'KeyTableController', 17 | controllerAs : 'keytable', 18 | templateUrl : 'directives/keytable.html', 19 | link: link 20 | }; 21 | return directive; 22 | 23 | function link(scope, element, attrs) { 24 | // get the value of the `ng-model` attribute 25 | scope.keytable.heading = attrs.heading; 26 | scope.keytable._onChange = $parse(attrs.ngChange); 27 | 28 | var variable = attrs.ngModel; 29 | scope.$watch(variable, function(model) { 30 | scope.keytable.reset(model); 31 | }); 32 | } 33 | } 34 | 35 | KeyTableController.$inject = ['$scope']; 36 | function KeyTableController($scope) { 37 | // HEAD // 38 | var vm = this; 39 | vm._onChange = null; 40 | vm.model = $scope.keytable.model || $scope.model || null; 41 | vm.rows = []; 42 | vm.add = add; 43 | vm.remove = remove; 44 | vm.change = change; 45 | vm.reset = reset; 46 | 47 | _activate(); 48 | 49 | // BODY // 50 | function _activate() { 51 | if (vm.model) { 52 | for (var key in vm.model) { 53 | add(key, vm.model[key], false); 54 | } 55 | } else { 56 | vm.model = {}; 57 | } 58 | } 59 | 60 | function reset(model) { 61 | vm.rows = []; 62 | vm.model = model; 63 | _activate(); 64 | } 65 | 66 | function add(key, value, fixed) { 67 | vm.rows.push({key:key, value:value, fixed:fixed===true}); 68 | } 69 | 70 | function remove(i) { 71 | vm.rows.splice(i, 1); 72 | } 73 | 74 | function change() { 75 | for (var key in vm.model){ 76 | if (vm.model.hasOwnProperty(key)){ 77 | delete vm.model[key]; 78 | } 79 | } 80 | for (var i=0; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/app/directives/tab.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3Tab', tab); 7 | 8 | function tab() { 9 | var directive = { 10 | require : '^b3Tabset', 11 | restrict : 'EA', 12 | scope : { 13 | active : '=?', 14 | heading : '@' 15 | }, 16 | transclude : true, 17 | templateUrl : 'directives/tab.html', 18 | link : link, 19 | }; 20 | return directive; 21 | 22 | function link(scope, element, attrs, ctrl) { 23 | scope.active = !!scope.active; 24 | ctrl.add(scope); 25 | } 26 | } 27 | 28 | })(); -------------------------------------------------------------------------------- /src/app/directives/tab.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /src/app/directives/tabset.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .directive('b3Tabset', tabset); 7 | 8 | function tabset() { 9 | var directive = { 10 | restrict : 'EA', 11 | transclude : true, 12 | replace : true, 13 | scope : {}, 14 | templateUrl : 'directives/tabset.html', 15 | bindToController : true, 16 | controllerAs : 'tabset', 17 | controller : tabsetController, 18 | }; 19 | return directive; 20 | } 21 | 22 | function tabsetController() { 23 | // HEAD // 24 | /* jshint validthis: true */ 25 | var vm = this; 26 | vm.tabs = []; 27 | vm.add = add; 28 | vm.select = select; 29 | 30 | // BODY // 31 | function add(tab) { 32 | vm.tabs.push(tab); 33 | } 34 | 35 | function select(tab) { 36 | angular.forEach(vm.tabs, function(t) { 37 | if (t.active && t !== tab) { 38 | t.active = false; 39 | } 40 | }); 41 | 42 | tab.active = true; 43 | } 44 | } 45 | 46 | 47 | })(); -------------------------------------------------------------------------------- /src/app/directives/tabset.html: -------------------------------------------------------------------------------- 1 |
2 | 7 | 8 | 9 |
-------------------------------------------------------------------------------- /src/app/models/project.model.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .factory('projectModel', projectModel); 7 | 8 | projectModel.$inject = [ 9 | '$q', 10 | '$rootScope', 11 | '$window', 12 | 'storageService', 13 | 'systemService', 14 | 'localStorageService', 15 | 'editorService' 16 | ]; 17 | 18 | function projectModel($q, 19 | $rootScope, 20 | $window, 21 | storageService, 22 | systemService, 23 | localStorageService, 24 | editorService) { 25 | 26 | // HEAD // 27 | var recentPath = systemService.join(systemService.getDataPath(), 'recents.json'); 28 | var recentCache = null; 29 | var currentProject = null; 30 | 31 | var service = { 32 | getRecentProjects : getRecentProjects, 33 | newProject : newProject, 34 | getProject : getProject, 35 | saveProject : saveProject, 36 | openProject : openProject, 37 | closeProject : closeProject, 38 | removeProject : removeProject, 39 | }; 40 | return service; 41 | 42 | // BODY // 43 | function _saveRecentProjects() { 44 | storageService.save(recentPath, recentCache); 45 | } 46 | function _updateRecentProjects(project) { 47 | if (project) { 48 | for (var i=recentCache.length-1; i>=0; i--) { 49 | if (recentCache[i].path === project.path) { 50 | recentCache.splice(i, 1); 51 | } else { 52 | recentCache[i].isOpen = false; 53 | } 54 | } 55 | 56 | var data = { 57 | name : project.name, 58 | description : project.description, 59 | path : project.path, 60 | isOpen : true, 61 | }; 62 | 63 | recentCache.splice(0, 0, data); 64 | } else { 65 | for (var j=0; j 2 |
3 | Editor 4 |
5 | 6 |
7 | 12 |
13 | 14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /src/app/pages/editor/components/menubar.html: -------------------------------------------------------------------------------- 1 | 170 | -------------------------------------------------------------------------------- /src/app/pages/editor/components/nodespanel.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('NodespanelController', NodespanelController); 7 | 8 | NodespanelController.$inject = [ 9 | '$scope', 10 | '$window', 11 | 'dialogService', 12 | 'notificationService' 13 | ]; 14 | 15 | function NodespanelController($scope, 16 | $window, 17 | dialogService, 18 | notificationService) { 19 | 20 | // HEAD // 21 | var vm = this; 22 | vm.nodes = null; 23 | vm.newTree = newTree; 24 | vm.select = select; 25 | vm.remove = remove; 26 | 27 | _create(); 28 | _activate(); 29 | $scope.$on('$destroy', _destroy); 30 | 31 | // BODY // 32 | function _activate() { 33 | vm.trees = []; 34 | vm.nodes = { 35 | composite : [], 36 | decorator : [], 37 | action : [], 38 | condition : [], 39 | }; 40 | 41 | var p = $window.editor.project.get(); 42 | p.nodes.each(function(node) { 43 | if (node.category === 'tree') return; 44 | 45 | var list = vm.nodes[node.category]; 46 | if (!list) return; 47 | list.push({ 48 | name: node.name, 49 | title: _getTitle(node), 50 | isDefault: node.isDefault 51 | }); 52 | }); 53 | 54 | var selected = p.trees.getSelected(); 55 | p.trees.each(function(tree) { 56 | var root = tree.blocks.getRoot(); 57 | vm.trees.push({ 58 | 'id' : tree._id, 59 | 'name' : root.title || 'A behavior tree', 60 | 'active' : tree===selected, 61 | }); 62 | }); 63 | } 64 | 65 | function _event(e) { 66 | setTimeout(function() {$scope.$apply(function() { _activate(); });}, 0); 67 | } 68 | 69 | function _create() { 70 | $window.editor.on('nodechanged', _event); 71 | $window.editor.on('noderemoved', _event); 72 | $window.editor.on('nodeadded', _event); 73 | $window.editor.on('treeadded', _event); 74 | $window.editor.on('blockchanged', _event); 75 | $window.editor.on('treeselected', _event); 76 | $window.editor.on('treeremoved', _event); 77 | $window.editor.on('treeimported', _event); 78 | } 79 | 80 | function _destroy() { 81 | $window.editor.off('nodechanged', _event); 82 | $window.editor.off('noderemoved', _event); 83 | $window.editor.off('nodeadded', _event); 84 | $window.editor.off('treeadded', _event); 85 | $window.editor.off('blockchanged', _event); 86 | $window.editor.off('treeselected', _event); 87 | $window.editor.off('treeremoved', _event); 88 | $window.editor.off('treeimported', _event); 89 | } 90 | 91 | function _getTitle(node) { 92 | var title = node.title || node.name; 93 | title = title.replace(/(<\w+>)/g, function(match, key) { return '@'; }); 94 | return title; 95 | } 96 | 97 | function newTree() { 98 | var p = $window.editor.project.get(); 99 | p.trees.add(); 100 | } 101 | 102 | function select(id) { 103 | var p = $window.editor.project.get(); 104 | p.trees.select(id); 105 | } 106 | 107 | function remove(id) { 108 | dialogService. 109 | confirm( 110 | 'Remove tree?', 111 | 'Are you sure you want to remove this tree?\n\nNote: all blocks using this tree will be removed.' 112 | ).then(function() { 113 | var p = $window.editor.project.get(); 114 | p.trees.remove(id); 115 | notificationService.success( 116 | 'Tree removed', 117 | 'The tree has been removed from this project.' 118 | ); 119 | }); 120 | } 121 | } 122 | })(); -------------------------------------------------------------------------------- /src/app/pages/editor/components/nodespanel.html: -------------------------------------------------------------------------------- 1 |
2 | 12 | 13 | 14 |
15 |
16 | 21 | 22 | 24 | Trees 25 | 26 |
27 | 28 |
29 |
30 | 31 | 49 |
50 |
51 | 52 |
53 | 58 | 59 | 61 | Nodes 62 | 63 |
64 | 65 |
66 |
67 |
{{category}}s
68 | 87 |
    88 |
  • empty
  • 89 |
90 |
91 | 92 |
93 |
94 | 95 |
-------------------------------------------------------------------------------- /src/app/pages/editor/components/propertiespanel.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('PropertiespanelController', PropertiespanelController); 7 | 8 | PropertiespanelController.$inject = [ 9 | '$scope', 10 | '$window' 11 | ]; 12 | 13 | function PropertiespanelController($scope, 14 | $window) { 15 | var vm = this; 16 | vm.original = null; 17 | vm.block = null; 18 | vm.update = update; 19 | vm.keydown = keydown; 20 | 21 | _create(); 22 | _activate(); 23 | 24 | $scope.$on('$destroy', _destroy); 25 | 26 | function _activate() { 27 | var p = $window.editor.project.get(); 28 | var t = p.trees.getSelected(); 29 | var s = t.blocks.getSelected(); 30 | 31 | if (s.length === 1) { 32 | vm.original = s[0]; 33 | vm.block = { 34 | title : vm.original.title, 35 | description : vm.original.description, 36 | properties : tine.merge({}, vm.original.properties) 37 | }; 38 | } else { 39 | vm.original = false; 40 | vm.block = false; 41 | } 42 | } 43 | function _event(e) { 44 | setTimeout(function() {$scope.$apply(function() { _activate(); });}, 0); 45 | 46 | } 47 | function _create() { 48 | $window.editor.on('blockselected', _event); 49 | $window.editor.on('blockdeselected', _event); 50 | $window.editor.on('blockremoved', _event); 51 | $window.editor.on('treeselected', _event); 52 | $window.editor.on('nodechanged', _event); 53 | } 54 | function _destroy() { 55 | $window.editor.off('blockselected', _event); 56 | $window.editor.off('blockdeselected', _event); 57 | $window.editor.off('blockremoved', _event); 58 | $window.editor.off('treeselected', _event); 59 | $window.editor.off('nodechanged', _event); 60 | } 61 | 62 | function keydown(e) { 63 | if (e.ctrlKey && e.keyCode == 90) { 64 | e.preventDefault(); 65 | } 66 | 67 | return false; 68 | } 69 | 70 | function update() { 71 | var p = $window.editor.project.get(); 72 | var t = p.trees.getSelected(); 73 | t.blocks.update(vm.original, vm.block); 74 | } 75 | } 76 | })(); -------------------------------------------------------------------------------- /src/app/pages/editor/components/propertiespanel.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
Properties
4 | 5 |
6 |

Select a single block to change its properties.

7 |

NOTE: The root node represents a tree. Therefore, changes applied to this node will persist on the tree object.

8 |
9 | 10 |
11 |
12 |
13 | 14 | 21 |
22 | 23 |
24 | 25 | 34 |
35 | 36 |
37 | 41 | 42 |
43 |
44 |
45 | 46 |
-------------------------------------------------------------------------------- /src/app/pages/editor/components/treespanel.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('TreespanelController', TreespanelController); 7 | 8 | TreespanelController.$inject = [ 9 | '$scope', 10 | '$window', 11 | 'dialogService', 12 | 'notificationService' 13 | ]; 14 | 15 | function TreespanelController($scope, 16 | $window, 17 | dialogService, 18 | notificationService) { 19 | 20 | // HEAD // 21 | var vm = this; 22 | vm.trees = null; 23 | vm.newTree = newTree; 24 | vm.select = select; 25 | vm.remove = remove; 26 | 27 | _create(); 28 | _activate(); 29 | $scope.$on('$destroy', _destroy); 30 | 31 | // BODY // 32 | function _activate() { 33 | vm.trees = []; 34 | 35 | var p = $window.editor.project.get(); 36 | var selected = p.trees.getSelected(); 37 | p.trees.each(function(tree) { 38 | var root = tree.blocks.getRoot(); 39 | vm.trees.push({ 40 | 'id' : tree._id, 41 | 'name' : root.title || 'A behavior tree', 42 | 'active' : tree===selected, 43 | }); 44 | }); 45 | } 46 | 47 | function _event(e) { 48 | if (e.type !== 'blockchanged' || e._target.category === 'root') { 49 | if (!$scope.$$phase) { 50 | $scope.$apply(function() { _activate(); }); 51 | } else { 52 | _activate(); 53 | } 54 | } 55 | } 56 | 57 | function _create() { 58 | $window.editor.on('blockchanged', _event); 59 | $window.editor.on('treeselected', _event); 60 | $window.editor.on('treeremoved', _event); 61 | $window.editor.on('treeimported', _event); 62 | } 63 | 64 | function _destroy() { 65 | $window.editor.off('blockchanged', _event); 66 | $window.editor.off('treeselected', _event); 67 | $window.editor.off('treeremoved', _event); 68 | $window.editor.off('treeimported', _event); 69 | } 70 | 71 | function newTree() { 72 | var p = $window.editor.project.get(); 73 | p.trees.add(); 74 | } 75 | 76 | function select(id) { 77 | var p = $window.editor.project.get(); 78 | p.trees.select(id); 79 | } 80 | 81 | function remove(id) { 82 | dialogService. 83 | confirm( 84 | 'Remove tree?', 85 | 'Are you sure you want to remove this tree?\n\nNote: all blocks using this tree will be removed.' 86 | ).then(function() { 87 | var p = $window.editor.project.get(); 88 | p.trees.remove(id); 89 | notificationService.success( 90 | 'Tree removed', 91 | 'The tree has been removed from this project.' 92 | ); 93 | }); 94 | 95 | } 96 | } 97 | })(); -------------------------------------------------------------------------------- /src/app/pages/editor/components/treespanel.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 10 | 11 |
12 |
13 | 29 |
30 |
31 |
-------------------------------------------------------------------------------- /src/app/pages/editor/editor.controller.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .controller('EditorController', EditorController); 4 | 5 | EditorController.$inject = []; 6 | 7 | function EditorController() { 8 | 9 | } -------------------------------------------------------------------------------- /src/app/pages/editor/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 |
16 |
17 |
-------------------------------------------------------------------------------- /src/app/pages/editor/modals/editnode.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('EditNodeController', EditNodeController); 7 | 8 | EditNodeController.$inject = [ 9 | '$scope', 10 | '$window', 11 | '$state', 12 | '$stateParams', 13 | 'dialogService', 14 | 'notificationService' 15 | ]; 16 | 17 | function EditNodeController($scope, 18 | $window, 19 | $state, 20 | $stateParams, 21 | dialogService, 22 | notificationService) { 23 | var vm = this; 24 | vm.action = 'New'; 25 | vm.node = null; 26 | vm.blacklist = null; 27 | vm.original = null; 28 | vm.save = save; 29 | vm.remove = remove; 30 | 31 | _active(); 32 | 33 | function _active() { 34 | var p = $window.editor.project.get(); 35 | 36 | if ($stateParams.name) { 37 | var node = p.nodes.get($stateParams.name); 38 | vm.node = node.copy(); 39 | vm.original = node; 40 | vm.action = 'Update'; 41 | } else { 42 | vm.node = new b3e.Node(); 43 | vm.node.category = 'composite'; 44 | } 45 | 46 | var blacklist = []; 47 | p.nodes.each(function(node) { 48 | if (node.name !== vm.node.name) { 49 | blacklist.push(node.name); 50 | } 51 | }); 52 | vm.blacklist = blacklist.join(','); 53 | } 54 | 55 | function save() { 56 | var p = $window.editor.project.get(); 57 | 58 | if (vm.original) { 59 | p.nodes.update(vm.original, vm.node); 60 | } else { 61 | p.nodes.add(vm.node); 62 | } 63 | 64 | $state.go('editor'); 65 | notificationService 66 | .success('Node created', 'Node has been created successfully.'); 67 | } 68 | 69 | function remove() { 70 | dialogService. 71 | confirm( 72 | 'Remove node?', 73 | 'Are you sure you want to remove this node?\n\nNote: all blocks using this node will be removed.' 74 | ).then(function() { 75 | var p = $window.editor.project.get(); 76 | p.nodes.remove(vm.original); 77 | notificationService.success( 78 | 'Node removed', 79 | 'The node has been removed from this project.' 80 | ); 81 | $state.go('editor'); 82 | }); 83 | } 84 | } 85 | 86 | })(); -------------------------------------------------------------------------------- /src/app/pages/editor/modals/editnode.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |

{{editnode.action}} node

7 |
8 | 9 |
10 |
11 | 12 | 22 | 23 |
24 | 25 |
26 | 27 | 31 |
32 | 33 |
34 | 35 | 43 |
44 |
45 | 46 |
47 |
48 | 49 | 50 |
51 | 52 |
53 | 54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 | 62 | 63 | 64 | 65 |
66 |
67 |
68 |
-------------------------------------------------------------------------------- /src/app/pages/editor/modals/export.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('ExportController', ExportController); 7 | 8 | ExportController.$inject = [ 9 | '$scope', 10 | '$document', 11 | '$window', 12 | '$stateParams', 13 | 'dialogService', 14 | 'notificationService', 15 | 'storageService' 16 | ]; 17 | 18 | function ExportController($scope, 19 | $document, 20 | $window, 21 | $stateParams, 22 | dialogService, 23 | notificationService, 24 | storageService) { 25 | var vm = this; 26 | vm.type = null; 27 | vm.format = null; 28 | vm.compact = ''; 29 | vm.pretty = ''; 30 | vm.result = null; 31 | vm.data = null; 32 | vm.hideCompact = false; 33 | vm.showCompact = showCompact; 34 | vm.showPretty = showPretty; 35 | vm.select = select; 36 | vm.save = save; 37 | 38 | _active(); 39 | 40 | function _active() { 41 | vm.type = $stateParams.type; 42 | vm.format = $stateParams.format; 43 | 44 | var e = $window.editor.export; 45 | 46 | if (vm.type === 'project' && vm.format === 'json') { 47 | _createJson(e.projectToData()); 48 | } 49 | else if (vm.type === 'tree' && vm.format === 'json') { 50 | _createJson(e.treeToData()); 51 | } 52 | else if (vm.type === 'nodes' && vm.format === 'json') { 53 | _createJson(e.nodesToData()); 54 | } 55 | } 56 | 57 | function _createJson(data) { 58 | vm.data = data; 59 | vm.compact = JSON3.stringify(data); 60 | vm.pretty = JSON3.stringify(data, null, 2); 61 | vm.result = vm.pretty; 62 | } 63 | 64 | function select(){ 65 | var range = $document[0].createRange(); 66 | range.selectNodeContents($document[0].getElementById('export-result')); 67 | var sel = $window.getSelection(); 68 | sel.removeAllRanges(); 69 | sel.addRange(range); 70 | } 71 | 72 | function save() { 73 | dialogService 74 | .saveAs(null, ['.b3', '.json']) 75 | .then(function(path) { 76 | storageService 77 | .saveAsync(path, vm.pretty) 78 | .then(function() { 79 | notificationService.success( 80 | 'File saved', 81 | 'The file has been saved successfully.' 82 | ); 83 | }); 84 | }); 85 | } 86 | 87 | function showCompact() { 88 | vm.result = vm.compact; 89 | } 90 | function showPretty() { 91 | vm.result = vm.pretty; 92 | } 93 | } 94 | 95 | })(); -------------------------------------------------------------------------------- /src/app/pages/editor/modals/export.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Export {{export.type}} as {{export.format}}

6 |
7 |
{{export.result||'Loading...'}}
8 |
9 |
10 | 11 |
12 | 17 | 23 | 29 | 34 | 38 |
39 |
40 |
-------------------------------------------------------------------------------- /src/app/pages/editor/modals/import.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('ImportController', ImportController); 7 | 8 | ImportController.$inject = [ 9 | '$scope', 10 | '$window', 11 | '$state', 12 | '$stateParams', 13 | 'dialogService', 14 | 'notificationService', 15 | 'storageService' 16 | ]; 17 | 18 | function ImportController($scope, 19 | $window, 20 | $state, 21 | $stateParams, 22 | dialogService, 23 | notificationService, 24 | storageService) { 25 | var vm = this; 26 | vm.type = null; 27 | vm.format = null; 28 | vm.open = open; 29 | vm.loadFromFile = loadFromFile; 30 | vm.data = ''; 31 | 32 | _active(); 33 | 34 | function _active() { 35 | vm.type = $stateParams.type; 36 | vm.format = $stateParams.format; 37 | } 38 | 39 | function loadFromFile() { 40 | dialogService 41 | .openFile(false, ['.b3', '.json']) 42 | .then(function(path) { 43 | storageService 44 | .loadAsync(path) 45 | .then(function(data) { 46 | vm.data = JSON3.stringify(data, null, 2); 47 | }); 48 | }); 49 | } 50 | function open() { 51 | var i = $window.editor.import; 52 | 53 | var data = JSON3.parse(vm.data); 54 | 55 | try { 56 | if (vm.type === 'project' && vm.format === 'json') { 57 | i.projectAsData(data); 58 | } 59 | else if (vm.type === 'tree' && vm.format === 'json') { 60 | i.treeAsData(data); 61 | } 62 | else if (vm.type === 'nodes' && vm.format === 'json') { 63 | i.nodesAsData(data); 64 | } 65 | } catch(e) { 66 | notificationService.error( 67 | 'Invalid data', 68 | 'The provided data is invalid.' 69 | ); 70 | } 71 | 72 | $state.go('editor'); 73 | } 74 | } 75 | 76 | })(); -------------------------------------------------------------------------------- /src/app/pages/editor/modals/import.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |

Import {{import.type}} from {{import.format}}

7 |
8 | 13 |
14 |
15 | 16 |
17 | 22 | 26 | 30 |
31 | 32 |
33 |
-------------------------------------------------------------------------------- /src/app/pages/editor/modals/modal.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Node

6 |
7 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo, animi officia minus iusto neque vitae esse voluptas qui. Laborum culpa quasi nisi fuga rerum, cupiditate quibusdam eos consequuntur! Libero, expedita.

8 |
9 |
10 | 11 |
12 | 13 | 14 |
15 |
16 |
-------------------------------------------------------------------------------- /src/app/pages/home/home.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('HomeController', HomeController); 7 | 8 | HomeController.$inject = [ 9 | ]; 10 | 11 | function HomeController() { 12 | var vm = this; 13 | 14 | _active(); 15 | 16 | function _active() { 17 | // var e = document.getElementById('prld'); 18 | // if (e) e.remove(); 19 | } 20 | } 21 | 22 | })(); -------------------------------------------------------------------------------- /src/app/pages/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |

Welcome to Behavior3 Editor

3 | 4 |
5 | 6 |

Behavior3 Editor is an open source visual tool to create and design Behavior Trees. It provides a general solution to model agents for games and other applications, such as simulations and robotics. Behavior3 Editor uses an open - and simple - format to describe the behavior trees, thus you can adapt easily to your own library, tool or framework.

7 | 8 |
9 | 10 |
11 |
12 | 18 | 24 |
25 |
26 | 27 |
28 | 29 |
30 |
SOFTWARE INFO
31 |
    32 |
  • 33 |
    34 |
    Software version:
    35 |
    [BUILD_VERSION]
    36 |
    37 |
  • 38 |
  • 39 |
    40 |
    Build date:
    41 |
    [BUILD_DATE]
    42 |
    43 |
  • 44 |
45 |
46 | 47 |
48 |
LINKS
49 | 75 |
76 | 77 | 105 | 106 |
107 |
108 | -------------------------------------------------------------------------------- /src/app/pages/projects/projects.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('ProjectsController', ProjectsController); 7 | 8 | ProjectsController.$inject = [ 9 | '$state', 10 | '$window', 11 | 'dialogService', 12 | 'systemService', 13 | 'notificationService', 14 | 'projectModel' 15 | ]; 16 | 17 | function ProjectsController($state, 18 | $window, 19 | dialogService, 20 | systemService, 21 | notificationService, 22 | projectModel) { 23 | 24 | // HEAD // 25 | var vm = this; 26 | vm.recentProjects = []; 27 | vm.isDesktop = null; 28 | 29 | vm.newProject = newProject; 30 | vm.openProject = openProject; 31 | vm.editProject = editProject; 32 | vm.saveProject = saveProject; 33 | vm.closeProject = closeProject; 34 | vm.removeProject = removeProject; 35 | 36 | _activate(); 37 | 38 | // BODY // 39 | function _activate() { 40 | vm.isDesktop = systemService.isDesktop; 41 | projectModel 42 | .getRecentProjects() 43 | .then(function(recents) { 44 | vm.recentProjects = recents; 45 | }); 46 | } 47 | 48 | function _newProject(path, name) { 49 | projectModel 50 | .newProject(path, name) 51 | .then(function() { 52 | $state.go('editor'); 53 | }); 54 | } 55 | 56 | function newProject() { 57 | function doNew() { 58 | // Get project name 59 | dialogService 60 | .prompt('New project', null, 'input', 'Project name') 61 | .then(function(name) { 62 | // If no name provided, abort 63 | if (!name) { 64 | notificationService.error( 65 | 'Invalid name', 66 | 'You must provide a name for the project.' 67 | ); 68 | return; 69 | } 70 | 71 | // If desktop, open file dialog 72 | if (vm.isDesktop) { 73 | var placeholder = name.replace(/\s+/g, "_").toLowerCase(); 74 | 75 | dialogService 76 | .saveAs(placeholder, ['.b3', '.json']) 77 | .then(function(path) { 78 | _newProject(path, name); 79 | }); 80 | } else { 81 | var path = 'b3projects-'+b3.createUUID(); 82 | _newProject(path, name); 83 | } 84 | }); 85 | } 86 | 87 | if ($window.editor.isDirty()) { 88 | dialogService 89 | .confirm( 90 | 'Leave without saving?', 91 | 'If you proceed you will lose all unsaved modifications.', 92 | null, {closeOnConfirm: false}) 93 | .then(doNew); 94 | } else { 95 | doNew(); 96 | } 97 | } 98 | 99 | function _openProject(path) { 100 | projectModel 101 | .openProject(path) 102 | .then(function() { 103 | $state.go('editor'); 104 | }, function() { 105 | notificationService.error( 106 | 'Invalid file', 107 | 'Couldn\'t open the project file.' 108 | ); 109 | }); 110 | } 111 | function openProject(path) { 112 | function doOpen() { 113 | if (path) { 114 | _openProject(path); 115 | } else { 116 | dialogService 117 | .openFile(false, ['.b3', '.json']) 118 | .then(function(path) { 119 | _openProject(path); 120 | }); 121 | } 122 | } 123 | 124 | if ($window.editor.isDirty()) { 125 | dialogService 126 | .confirm( 127 | 'Leave without saving?', 128 | 'If you proceed you will lose all unsaved modifications.') 129 | .then(doOpen); 130 | } else { 131 | doOpen(); 132 | } 133 | } 134 | 135 | function editProject() { 136 | var project = projectModel.getProject(); 137 | 138 | dialogService 139 | .prompt('Rename project', null, 'input', project.name) 140 | .then(function(name) { 141 | // If no name provided, abort 142 | if (!name) { 143 | notificationService.error( 144 | 'Invalid name', 145 | 'You must provide a name for the project.' 146 | ); 147 | return; 148 | } 149 | 150 | project.name = name; 151 | projectModel 152 | .saveProject(project) 153 | .then(function() { 154 | _activate(); 155 | notificationService.success( 156 | 'Project renamed', 157 | 'The project has been renamed successfully.' 158 | ); 159 | }); 160 | }); 161 | } 162 | 163 | function saveProject() { 164 | projectModel 165 | .saveProject() 166 | .then(function() { 167 | notificationService.success( 168 | 'Project saved', 169 | 'The project has been saved' 170 | ); 171 | }, function() { 172 | notificationService.error( 173 | 'Error', 174 | 'Project couldn\'t be saved' 175 | ); 176 | }); 177 | } 178 | 179 | function closeProject() { 180 | function doClose() { 181 | projectModel.closeProject(); 182 | } 183 | 184 | if ($window.editor.isDirty()) { 185 | dialogService 186 | .confirm( 187 | 'Leave without saving?', 188 | 'If you proceed you will lose all unsaved modifications.', 189 | null) 190 | .then(doClose); 191 | } else { 192 | doClose(); 193 | } 194 | } 195 | 196 | function removeProject(path) { 197 | dialogService. 198 | confirm( 199 | 'Remove project?', 200 | 'Are you sure you want to remove this project?' 201 | ).then(function() { 202 | projectModel 203 | .removeProject(path) 204 | .then(function() { 205 | _activate(); 206 | notificationService.success( 207 | 'Project removed', 208 | 'The project has been removed from editor.' 209 | ); 210 | }); 211 | }); 212 | } 213 | } 214 | })(); -------------------------------------------------------------------------------- /src/app/pages/projects/projects.html: -------------------------------------------------------------------------------- 1 |
2 |

Projects

3 | 4 | 12 | 13 |
14 | 15 | 16 |
17 |

You don't have any project yet.

18 | 19 |
20 | 21 | 22 | 23 | 24 | 35 | 42 | 43 |
25 |
26 | 27 | 28 | 29 | 30 |
31 | 32 | Current project 33 |

{{item.name}}

34 |
36 |
37 | 38 | 39 |
40 |

{{item.name}}

41 |
44 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /src/app/pages/settings/settings.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('app') 6 | .controller('SettingsController', SettingsController); 7 | 8 | SettingsController.$inject = [ 9 | 'notificationService', 10 | 'settingsModel', 11 | 'dialogService', 12 | ]; 13 | 14 | function SettingsController(notificationService, 15 | settingsModel, 16 | dialogService) { 17 | 18 | // HEADER // 19 | var vm = this; 20 | vm.settings = {}; 21 | vm.saveSettings = saveSettings; 22 | vm.resetSettings = resetSettings; 23 | 24 | _activate(); 25 | 26 | // BODY // 27 | function _activate() { 28 | settingsModel 29 | .getSettings() 30 | .then(function(settings) { 31 | vm.settings = settings; 32 | }); 33 | } 34 | 35 | function saveSettings() { 36 | settingsModel 37 | .saveSettings(vm.settings) 38 | .then(function() { 39 | notificationService.success( 40 | 'Settings saved', 41 | 'The editor settings has been updated.' 42 | ); 43 | }); 44 | } 45 | 46 | function resetSettings() { 47 | dialogService.confirm( 48 | 'Reset Settings?', 49 | 'Are you sure you want to reset to the default settings?' 50 | ).then(function() { 51 | settingsModel 52 | .resetSettings() 53 | .then(function() { 54 | notificationService.success( 55 | 'Settings reseted', 56 | 'The editor settings has been updated to default values.' 57 | ); 58 | _activate(); 59 | }); 60 | }); 61 | } 62 | } 63 | })(); -------------------------------------------------------------------------------- /src/app/services/dialog.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('dialogService', dialogService); 4 | 5 | dialogService.$inject = ['$window', '$q', '$document', 'nodejsService']; 6 | 7 | function dialogService($window, $q, $document, nodejsService) { 8 | var service = { 9 | alert : alert, 10 | confirm : confirm, 11 | prompt : prompt, 12 | saveAs : saveAs, 13 | openFile : openFile, 14 | openDirectory : openDirectory 15 | }; 16 | return service; 17 | 18 | function _callFileDialog(dialog) { 19 | return $q(function(resolve) { 20 | dialog.addEventListener('change', function() { 21 | resolve(dialog.value); 22 | }); 23 | dialog.click(); 24 | }); 25 | } 26 | 27 | function alert(title, text, type, options) { 28 | options = options || {}; 29 | options.title = title; 30 | options.text = text; 31 | options.type = type; 32 | options.customClass = type; 33 | 34 | return $q(function(resolve) { swal(options, function() { resolve(); }); }); 35 | } 36 | function confirm(title, text, type, options) { 37 | options = options || {}; 38 | options.title = title; 39 | options.text = text; 40 | options.type = type; 41 | options.customClass = type; 42 | options.showCancelButton = true; 43 | 44 | return $q(function(resolve, reject) { 45 | $window.swal(options, function(ok) { 46 | if (ok) { 47 | resolve(); 48 | } else { 49 | reject(); 50 | } 51 | }); 52 | }); 53 | } 54 | function prompt(title, text, type, placeholder, options) { 55 | options = options || {}; 56 | options.title = title; 57 | options.text = text; 58 | options.type = type || 'input'; 59 | options.inputPlaceholder = placeholder; 60 | options.customClass = type; 61 | options.showCancelButton = true; 62 | 63 | return $q(function(resolve, reject) { 64 | swal(options, function(val) { 65 | if (val!==false) { 66 | resolve(val); 67 | } else { 68 | reject(val); 69 | } 70 | }); 71 | }); 72 | } 73 | function saveAs(placeholder, types) { 74 | return $q(function(resolve, reject) { 75 | var value = nodejsService.dialog.showSaveDialog({ 76 | title: 'Save project as...', 77 | defaultPath: placeholder + '.b3', 78 | filters : [ 79 | {name: 'Behavior3 File', extensions: ['b3', 'json']}, 80 | {name: 'All Files', extensions: ['*']} 81 | ] 82 | }); 83 | if (value) { 84 | resolve(value); 85 | } else { 86 | reject(); 87 | } 88 | }); 89 | } 90 | function openFile(multiple, types) { 91 | return $q(function(resolve, reject) { 92 | var value = nodejsService.dialog.showOpenDialog({ 93 | title: 'Open file...', 94 | multiSelections: multiple, 95 | properties: ['openFile'], 96 | filters : [ 97 | {name: 'Behavior3 File', extensions: ['b3', 'json']}, 98 | {name: 'All Files', extensions: ['*']} 99 | ] 100 | }); 101 | 102 | if (value) { 103 | if (!multiple) { 104 | value = value[0]; 105 | } 106 | console.log(value); 107 | resolve(value); 108 | } else { 109 | reject(); 110 | } 111 | }); 112 | } 113 | function openDirectory() { 114 | return $q(function(resolve, reject) { 115 | var value = nodejsService.dialog.showOpenDialog({ 116 | title: 'Open directory...', 117 | properties: ['openDirectory'] 118 | }); 119 | if (value) { 120 | resolve(value); 121 | } else { 122 | reject(); 123 | } 124 | }); 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /src/app/services/editor.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('editorService', editorService); 4 | 5 | editorService.$inject = ['$window']; 6 | 7 | function editorService($window) { 8 | var service = { 9 | getDefaultSettings : getDefaultSettings, 10 | applySettings : applySettings, 11 | newProject : newProject, 12 | openProject : openProject, 13 | closeProject : closeProject, 14 | exportProject : exportProject, 15 | }; 16 | return service; 17 | 18 | function getDefaultSettings() { 19 | return $window.b3e.DEFAULT_SETTINGS; 20 | } 21 | function applySettings(settings) { 22 | $window.editor.applySettings(settings); 23 | } 24 | 25 | function newProject() { 26 | $window.editor.project.create(); 27 | } 28 | function openProject(data) { 29 | $window.editor.project.open(data); 30 | } 31 | function closeProject() { 32 | $window.editor.project.close(); 33 | } 34 | 35 | function exportProject() { 36 | return $window.editor.export.projectToData(); 37 | } 38 | } -------------------------------------------------------------------------------- /src/app/services/nodejs.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('nodejsService', nodejsService); 4 | 5 | nodejsService.$inject = ['$window']; 6 | 7 | function nodejsService($window) { 8 | var ok = !!$window.require; 9 | var remote = (ok?$window.require('remote'):null); 10 | var service = { 11 | ok : ok, 12 | fs : (ok?$window.require('fs'):null), 13 | path : (ok?$window.require('path'):null), 14 | dialog : (ok?remote.require('dialog'):null), 15 | }; 16 | return service; 17 | 18 | } -------------------------------------------------------------------------------- /src/app/services/notification.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('notificationService', notificationService); 4 | 5 | notificationService.$inject = ['$window', '$timeout', '$compile', '$rootScope', '$sce']; 6 | 7 | function notificationService($window, $timeout, $compile, $rootScope, $sce) { 8 | var elementBuffer = []; 9 | var service = { 10 | notify : notify, 11 | simple : simple, 12 | success : success, 13 | error : error, 14 | info : info, 15 | warning : warning, 16 | }; 17 | return service; 18 | 19 | function _reposite() { 20 | var verticalSpacing = 10; 21 | var lastBottom = 20; 22 | 23 | for(var i=elementBuffer.length-1; i>=0; i--) { 24 | var element = elementBuffer[i]; 25 | var height = parseInt(element[0].offsetHeight); 26 | 27 | var bottom = lastBottom; 28 | lastBottom = bottom+height+verticalSpacing; 29 | 30 | element.css('bottom', bottom+'px'); 31 | } 32 | } 33 | 34 | function _note(config) { 35 | var TEMPLATE = ''+ 36 | '
'+ 37 | '
'+ 38 | '
' + 39 | '
'+ 40 | '
'+ 41 | '
' + 42 | '
'; 43 | 44 | var DEFAULT = { 45 | type : 'default', 46 | title : '', 47 | message : '', 48 | icon : false, 49 | delay : 3000, 50 | }; 51 | 52 | // Default parameters 53 | config = tine.merge({}, DEFAULT, config); 54 | 55 | // Set scope variables to fill the template 56 | var scope = $rootScope.$new(); 57 | scope.type = config.type; 58 | scope.title = $sce.trustAsHtml(config.title); 59 | scope.message = $sce.trustAsHtml(config.message); 60 | scope.icon = config.icon; 61 | scope.delay = config.delay; 62 | 63 | // Create the DOM element and add events 64 | var element = $compile(TEMPLATE)(scope); 65 | element.bind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd click', function(e) { 66 | e = e.originalEvent || e; 67 | if (e.type==='click' || element.hasClass('killed')) { 68 | element.remove(); 69 | elementBuffer.remove(element); 70 | _reposite(); 71 | } 72 | }); 73 | 74 | if (angular.isNumber(config.delay)) { 75 | $timeout(function() { 76 | element.addClass('killed'); 77 | }, config.delay); 78 | } 79 | 80 | $timeout(function() { 81 | element.addClass('started'); 82 | _reposite(); 83 | }, 0); 84 | 85 | elementBuffer.push(element); 86 | angular.element(document.getElementsByTagName('body')).append(element); 87 | } 88 | 89 | function notify(config) { 90 | _note(config); 91 | } 92 | function simple(title, message) { 93 | _note({title:title, message:message, type:'default'}); 94 | } 95 | function success(title, message) { 96 | _note({title:title, message:message, icon:'fa-check', type:'success'}); 97 | } 98 | function error(title, message) { 99 | _note({title:title, message:message, icon:'fa-close', type:'error'}); 100 | } 101 | function info(title, message) { 102 | _note({title:title, message:message, icon:'fa-info', type:'info'}); 103 | } 104 | function warning(title, message) { 105 | _note({title:title, message:message, icon:'fa-warning', type:'warning'}); 106 | } 107 | } -------------------------------------------------------------------------------- /src/app/services/storage/filestorage.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('fileStorageService', fileStorageService); 4 | 5 | fileStorageService.$inject = ['nodejsService']; 6 | 7 | function fileStorageService(nodejsService) { 8 | var ok = nodejsService.ok; 9 | var fs = nodejsService.fs; 10 | 11 | var service = { 12 | ok : ok, 13 | save : save, 14 | load : load, 15 | remove : remove 16 | }; 17 | return service; 18 | 19 | function save(path, data) { 20 | if (typeof data !== 'string') { 21 | try { data = JSON3.stringify(data); } catch (e) {} 22 | } 23 | 24 | var file = fs.openSync(path+'~', 'w'); 25 | fs.writeSync(file, data); 26 | fs.closeSync(file); 27 | 28 | // Rename must be async to override correctly. 29 | fs.rename(path+'~', path); 30 | } 31 | function load(path) { 32 | var data = fs.readFileSync(path, 'utf-8'); 33 | try { data = JSON3.parse(data); } catch (e) {} 34 | return data; 35 | } 36 | function remove(path) { 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /src/app/services/storage/localstorage.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('localStorageService', localStorageService); 4 | 5 | localStorageService.$inject = ['$window']; 6 | 7 | function localStorageService($window) { 8 | var service = { 9 | ok : !!$window.localStorage, 10 | save : save, 11 | load : load, 12 | remove : remove 13 | }; 14 | return service; 15 | 16 | function save(path, data) { 17 | try { data = JSON.stringify(data); } catch (e) {} 18 | $window.localStorage[path] = data; 19 | } 20 | function load(path) { 21 | var data = $window.localStorage[path]; 22 | try { data = JSON.parse(data); } catch (e) {} 23 | return data; 24 | } 25 | function remove(path) { 26 | delete $window.localStorage[path]; 27 | } 28 | } -------------------------------------------------------------------------------- /src/app/services/storage/storage.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('storageService', storageService); 4 | 5 | storageService.$inject = ['$q', 'localStorageService', 'fileStorageService']; 6 | 7 | function storageService($q, localStorageService, fileStorageService) { 8 | var storage = (fileStorageService.ok?fileStorageService:localStorageService); 9 | 10 | var service = { 11 | save : save, 12 | saveAsync : saveAsync, 13 | load : load, 14 | loadAsync : loadAsync, 15 | remove : remove, 16 | removeAsync : removeAsync, 17 | }; 18 | return service; 19 | 20 | function save(path, data) { 21 | storage.save(path, data); 22 | } 23 | function saveAsync(path, data) { 24 | return $q(function(resolve, reject) { 25 | try { 26 | storage.save(path, data); 27 | resolve(); 28 | } catch (e) { 29 | reject(e); 30 | } 31 | }); 32 | } 33 | function load(path) { 34 | return storage.load(path); 35 | } 36 | function loadAsync(path) { 37 | return $q(function(resolve, reject) { 38 | try { 39 | var data = storage.load(path); 40 | resolve(data); 41 | } catch (e) { 42 | reject(e); 43 | } 44 | }); 45 | } 46 | function remove(path) { 47 | storage.remove(path); 48 | } 49 | function removeAsync(path) { 50 | return $q(function(resolve, reject) { 51 | try { 52 | storage.remove(path); 53 | resolve(); 54 | } catch (e) { 55 | reject(e); 56 | } 57 | }); 58 | } 59 | } -------------------------------------------------------------------------------- /src/app/services/system.service.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app') 3 | .factory('systemService', systemService); 4 | 5 | systemService.$inject = ['$window', 'nodejsService']; 6 | 7 | function systemService($window, nodejsService) { 8 | var isDesktop = !!$window.process; 9 | var service = { 10 | isDesktop : isDesktop, 11 | getDataPath : getDataPath, 12 | join : join, 13 | }; 14 | return service; 15 | 16 | function _createIfNonExist(path) { 17 | try { 18 | var s = nodejsService.fs.statSync(path); 19 | } catch (e) { 20 | nodejsService.fs.mkdirSync(path); 21 | } 22 | } 23 | function getDataPath() { 24 | if (isDesktop) { 25 | var datapath = process.env.APPDATA; 26 | if (!datapath) { 27 | datapath = process.env.HOME + '/.behavior3'; 28 | } 29 | 30 | var path = join(datapath, 'b3editor'); 31 | _createIfNonExist(datapath); 32 | _createIfNonExist(path); 33 | return path; 34 | } else { 35 | return 'b3editor'; 36 | } 37 | } 38 | function join() { 39 | if (isDesktop) { 40 | return nodejsService.path.join.apply(nodejsService.path, arguments); 41 | } else { 42 | var s = arguments[0]; 43 | for (var i=1; i model validation 20 | ctrl.$parsers.unshift(function(value) { 21 | var valid = blacklist.indexOf(value) === -1; 22 | ctrl.$setValidity('blacklist', valid); 23 | return valid ? value : undefined; 24 | }); 25 | 26 | //For model -> DOM validation 27 | ctrl.$formatters.unshift(function(value) { 28 | ctrl.$setValidity('blacklist', blacklist.indexOf(value) === -1); 29 | return value; 30 | }); 31 | } 32 | } 33 | 34 | })(); -------------------------------------------------------------------------------- /src/assets/css/preload.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | padding: 0px; 3 | margin: 0px; 4 | overflow: hidden; 5 | line-height: 1.42857143; 6 | } 7 | 8 | .preloading { 9 | position : absolute; 10 | top : 0; 11 | left : 0; 12 | width : 100%; 13 | height : 100%; 14 | color : #999999; 15 | background : #171717; 16 | z-index : 10000000; 17 | text-align : center; 18 | font-size : 16px !important; 19 | font-family : "Raleway", sans-serif !important; 20 | font-weight: 300 !important; 21 | 22 | -webkit-transition : opacity .5s ease-in-out; 23 | -moz-transition : opacity .5s ease-in-out; 24 | -o-transition : opacity .5s ease-in-out; 25 | transition : opacity .5s ease-in-out; 26 | } 27 | .preloading.preload-fade { 28 | opacity: 0; 29 | } 30 | 31 | .preloading-body { 32 | width : 50vw; 33 | margin : 45vh auto; 34 | } 35 | 36 | .preloading-body-title { 37 | font-size : 2.0em; 38 | margin : 0px; 39 | display : block; 40 | } 41 | 42 | .preloading-body-subtitle { 43 | font-size : 0.9em; 44 | opacity : 0.8; 45 | display : block; 46 | } 47 | -------------------------------------------------------------------------------- /src/assets/imgs/closedhand.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/behavior3/behavior3editor/20c2202c7ca9616dbbd5146f87f3f8df3d3567f5/src/assets/imgs/closedhand.cur -------------------------------------------------------------------------------- /src/assets/imgs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/behavior3/behavior3editor/20c2202c7ca9616dbbd5146f87f3f8df3d3567f5/src/assets/imgs/favicon.ico -------------------------------------------------------------------------------- /src/assets/js/preload.js: -------------------------------------------------------------------------------- 1 | function preloadProgress(message) { 2 | var element = document.getElementById('page-preload-progress'); 3 | element.innerHTML = message; 4 | } -------------------------------------------------------------------------------- /src/assets/less/animations.less: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | // NGVIEW 4 | [ui-view].ng-enter, [ui-view].ng-leave { 5 | position:absolute; 6 | height:100%; 7 | width:100%; 8 | .animation-fast; 9 | } 10 | 11 | // APP 12 | [ui-view].app-anim.ng-enter { opacity: 0; } 13 | [ui-view].app-anim.ng-enter-active { opacity: 1; } 14 | [ui-view].app-anim.ng-leave { opacity: 1; } 15 | [ui-view].app-anim.ng-leave-active { opacity: 0; } 16 | 17 | [ui-view].app-anim.ng-enter .sidebar.right, 18 | [ui-view].app-anim.ng-leave .sidebar.right { .animation-fast; } 19 | [ui-view].app-anim.ng-enter .sidebar.right { right: -@size-sidebar; } 20 | [ui-view].app-anim.ng-enter-active .sidebar.right { right: 0; } 21 | [ui-view].app-anim.ng-leave .sidebar.right { right: 0; } 22 | [ui-view].app-anim.ng-leave-active .sidebar.right { right: -@size-sidebar; } 23 | 24 | // DASH 25 | [ui-view].dash-anim.ng-enter { opacity: 0; } 26 | [ui-view].dash-anim.ng-enter-active { opacity: 1; } 27 | [ui-view].dash-anim.ng-leave { opacity: 1; } 28 | [ui-view].dash-anim.ng-leave-active { opacity: 0; } 29 | 30 | // EDITOR 31 | [ui-view].editor-anim.ng-enter .b3modal, 32 | [ui-view].editor-anim.ng-leave .b3modal { .animation-fast; } 33 | [ui-view].editor-anim.ng-enter { opacity: 0; z-index: @z-modal; } 34 | [ui-view].editor-anim.ng-enter-active { opacity: 1; } 35 | [ui-view].editor-anim.ng-leave { opacity: 1; } 36 | [ui-view].editor-anim.ng-leave-active { opacity: 0; } 37 | 38 | [ui-view].editor-anim.ng-enter .b3modal-window, 39 | [ui-view].editor-anim.ng-leave .b3modal-window { 40 | .animation-fast; 41 | transform-origin: left top; 42 | } 43 | // [ui-view].editor-anim.ng-enter .b3modal-window { transform: scale(0.1, 1); } 44 | // [ui-view].editor-anim.ng-enter-active .b3modal-window { transform: scale(1, 1); } 45 | // [ui-view].editor-anim.ng-leave .b3modal-window { transform: scale(1, 1); } 46 | // [ui-view].editor-anim.ng-leave-active .b3modal-window { transform: scale(0.1, 1); } 47 | 48 | 49 | 50 | 51 | // // HIDE 52 | // .anim { .animation } 53 | // .anim.ng-hide { opacity:0; } 54 | 55 | // // INVISIBLE 56 | // .invisible-add, .invisible-remove { .animation-fast } 57 | 58 | // .invisible, .invisible-add.invisible-add-active { opacity: 0; } 59 | // .invisible-remove.css-class-remove-active { opacity: 1; } -------------------------------------------------------------------------------- /src/assets/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .nav, .pagination, .carousel, .panel-title a { cursor: pointer; } 4 | 5 | .panel { 6 | .title { margin-top: 0px; } 7 | .panel-heading { 8 | h1, h2, h3, h4 { margin: 0px; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/assets/less/c_app.less: -------------------------------------------------------------------------------- 1 | /** 2 | * APP GLOBALS 3 | */ 4 | 5 | html, body { 6 | padding: 0px; 7 | margin: 0px; 8 | overflow: hidden; 9 | font-size: 16px; 10 | color: @color-light1; 11 | background: @color-dark1; 12 | 13 | .font-base; 14 | } 15 | 16 | .grabbing { 17 | cursor: url(../imgs/closedhand.cur); 18 | cursor: url(../imgs/closedhand.cur) 8 8, move; 19 | } 20 | .no-margin { margin: 0px; } 21 | .no-padding { padding: 0px; } 22 | .sweet-alert input { color: @color-dark1; } 23 | .full-height { height: 100%; } 24 | .no-select{ 25 | -webkit-touch-callout: none; 26 | -webkit-user-select: none; 27 | -khtml-user-select: none; 28 | -moz-user-select: none; 29 | -ms-user-select: none; 30 | user-select: none; 31 | } 32 | 33 | a, .btn, .btn-success, .btn-default, .btn-warning, .btn-info, .btn-danger { 34 | .no-select; 35 | } 36 | 37 | .form-control[disabled], 38 | .form-control[readonly], 39 | fieldset[disabled] .form-control { 40 | cursor: inherit; 41 | } 42 | 43 | // LINKS ====================================================================== 44 | a { 45 | color: @color-blue-dark; 46 | transition: color 100ms ease-in-out; 47 | outline: none; 48 | 49 | &:hover, &:visited, &:link, &:active, &:focus { 50 | text-decoration: none; 51 | outline: none; 52 | } 53 | &:hover { 54 | cursor: pointer; 55 | color: @color-blue; 56 | } 57 | } 58 | 59 | // SCROLLBAR ================================================================== 60 | ::-webkit-scrollbar { 61 | width: 10px; 62 | background: none; 63 | } 64 | ::-webkit-scrollbar-track { 65 | background: none; 66 | } 67 | ::-webkit-scrollbar-thumb { 68 | background: @color-blue-light; 69 | } 70 | ::-webkit-scrollbar-thumb:window-inactive { 71 | background: @color-blue-light; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/assets/less/c_dash.less: -------------------------------------------------------------------------------- 1 | .dash-menu { 2 | list-style: none; 3 | text-align: right; 4 | margin-right: @padding-large; 5 | font-size: 2em; 6 | .font-header; 7 | 8 | li { 9 | margin-bottom: 10px; 10 | 11 | a { 12 | color: @color-light2; 13 | &.active { color: @color-light1; } 14 | &:hover { color: @color-light1; } 15 | } 16 | } 17 | } 18 | 19 | .dash-page { 20 | overflow: auto; 21 | position: absolute; 22 | top: 0px; 23 | left: 0px; 24 | width: 100%; 25 | height: 100%; 26 | z-index: @z-page; 27 | background-color: @color-light1; 28 | } -------------------------------------------------------------------------------- /src/assets/less/c_keytable.less: -------------------------------------------------------------------------------- 1 | .keytable { 2 | 3 | input[type=button] { 4 | width: 40px; 5 | } 6 | 7 | th { 8 | margin: 0px; 9 | padding: 0px 2px !important; 10 | 11 | label { 12 | padding-bottom: 0px; 13 | } 14 | } 15 | 16 | td { 17 | border: 0px !important; 18 | padding: 2px 2px !important; 19 | 20 | input:not([type=button]) { 21 | padding: 0px @padding-small; 22 | margin: 0px; 23 | } 24 | } 25 | 26 | .form-control { 27 | height: 25px; 28 | } 29 | 30 | &.no-border { 31 | th { 32 | border: 0px; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/assets/less/c_menubar.less: -------------------------------------------------------------------------------- 1 | .menubar { 2 | position: absolute; 3 | top: 0px; 4 | width: 100%; 5 | height: @size-menubar; 6 | z-index: @z-menubar; 7 | color: @color-light1; 8 | font-size: 14px; 9 | background-color: @color-dark4; 10 | border-bottom: 1px solid @color-dark2; 11 | 12 | .box-shadow(0px 0px 16px 5px); 13 | 14 | &-left, &-right { 15 | display: inline; 16 | } 17 | 18 | &-right { 19 | float: right; 20 | } 21 | } 22 | 23 | .menubar ul { 24 | padding: 0px; 25 | margin: 0px; 26 | list-style: none; 27 | // position: relative; 28 | display: inline-table; 29 | } 30 | .menubar ul:after { 31 | content: ""; 32 | clear: both; 33 | display: block; 34 | } 35 | .menubar ul li { 36 | float: left; 37 | } 38 | .menubar ul li:hover { 39 | background-color: @color-dark1; 40 | } 41 | .menubar ul li a { 42 | color: #FFFFFF; 43 | display: block; 44 | height: 35px; 45 | padding: 0px 10px; 46 | line-height: 35px; 47 | text-decoration: none; 48 | .no-select; 49 | 50 | .shortcut { 51 | color: #999; 52 | float: right; 53 | font-style: italic; 54 | } 55 | } 56 | .menubar .side { 57 | // width: @size-sidebar; 58 | text-align: left; 59 | display: inline-block; 60 | 61 | a { 62 | display: inline-block; 63 | 64 | &.fastlink { 65 | color: @color-light1; 66 | padding: 0px 6px 0px 7px !important; 67 | border-right: 1px solid @color-dark2; 68 | } 69 | } 70 | 71 | .logo { 72 | color: #4BB2FD; 73 | background-color: #333333; 74 | // border-left: 1px solid @color-dark2; 75 | // border-right: 1px solid @color-dark2; 76 | font-weight: bold; 77 | 78 | .animation-slow; 79 | 80 | &:hover { 81 | color: @color-dark2; 82 | background-color: @color-light2; 83 | } 84 | } 85 | } 86 | 87 | .menubar li.disabled { 88 | a { 89 | color: @color-light3; 90 | cursor: default; 91 | } 92 | &:hover { 93 | background-color: inherit; 94 | } 95 | } 96 | 97 | .menubar ul li:hover > ul { 98 | display: block; 99 | } 100 | .menubar ul ul { 101 | display: none; 102 | background: #454545; 103 | position: absolute; 104 | top: 100%; 105 | min-width: 235px; 106 | border: 1px solid #333333; 107 | } 108 | .menubar .menubar-right ul ul { 109 | right: 0; 110 | } 111 | .menubar ul ul li { 112 | float: none; 113 | position: relative; 114 | } 115 | .menubar ul ul li a { 116 | height: 35px; 117 | padding: 0px 10px; 118 | line-height: 35px; 119 | color: #fff; 120 | text-align: left; 121 | } 122 | .menubar ul ul ul { 123 | position: absolute; 124 | top: 0; 125 | } 126 | .menubar .menubar-left ul ul ul { left: 100%; } 127 | .menubar .menubar-right ul ul ul { right: 100%; } 128 | .menubar .divider { border-top: 1px solid #555; } 129 | 130 | .arrow-right { 131 | width: 0; 132 | height: 0; 133 | margin-top: 12px; 134 | border-top: 5px solid transparent; 135 | border-bottom: 5px solid transparent; 136 | border-left: 5px solid #FFF; 137 | } 138 | 139 | 140 | [ui-view].ng-enter .menubar, 141 | [ui-view].ng-leave .menubar { .animation-fast; } 142 | [ui-view].ng-enter .menubar { top: -@size-menubar; } 143 | [ui-view].ng-enter-active .menubar { top: 0; } 144 | [ui-view].ng-leave .menubar { top: 0; } 145 | [ui-view].ng-leave-active .menubar { top: -@size-menubar; } -------------------------------------------------------------------------------- /src/assets/less/c_modal.less: -------------------------------------------------------------------------------- 1 | .b3modal-top { 2 | position: fixed; 3 | top: 0px; 4 | left: 0px; 5 | right: 0px; 6 | bottom: 0px; 7 | } 8 | 9 | .b3modal { 10 | z-index: @z-modal; 11 | 12 | &-background { 13 | .b3modal-top; 14 | z-index: @z-modal; 15 | background: @color-dark1; 16 | opacity: 0.9; 17 | } 18 | 19 | &-window { 20 | .b3modal-top; 21 | top: @size-menubar; 22 | left: @size-sidebar; 23 | right: @size-sidebar; 24 | bottom: @size-menubar; 25 | z-index: @z-modal+1; 26 | 27 | background-color: @color-light1; 28 | color: @color-dark1; 29 | overflow: hidden; 30 | padding-bottom: 75px; 31 | 32 | .box-shadow(0px 0px 16px 0px); 33 | } 34 | 35 | &-wrap { 36 | overflow-y: auto; 37 | height: 100%; 38 | } 39 | 40 | &-title { 41 | color: @color-light3; 42 | margin: 0px; 43 | padding: @padding-large; 44 | padding-bottom: 0px; 45 | font-size: 2.5em; 46 | .font-header; 47 | } 48 | 49 | &-content { 50 | padding: @padding-large; 51 | } 52 | 53 | &-buttons { 54 | position: absolute; 55 | bottom: 0px; 56 | height: 75px; 57 | width: 100%; 58 | text-align: right; 59 | padding: @padding-medium @padding-large; 60 | background-color: @color-light2; 61 | } 62 | } 63 | 64 | .serialize-result { 65 | overflow: auto; 66 | border: 1px solid @color-light3; 67 | height: 100%; 68 | padding: @padding-medium; 69 | } -------------------------------------------------------------------------------- /src/assets/less/c_nodes.less: -------------------------------------------------------------------------------- 1 | .node-list { 2 | .empty { 3 | padding: 0px @padding-small; 4 | color: @color-light2; 5 | } 6 | 7 | &-content { 8 | padding: 0px @padding-small; 9 | } 10 | 11 | &-title { 12 | cursor: pointer; 13 | display: block; 14 | margin-top: 10px; 15 | padding: 2px 5px; 16 | // text-transform: uppercase; 17 | text-transform: capitalize; 18 | color: @color-light3; 19 | // border-bottom: 1px solid @color-dark4; 20 | .font-base; 21 | } 22 | 23 | &-category { 24 | padding: 0px 0px @padding-small-2x 0px; 25 | 26 | ul { 27 | list-style: none; 28 | margin: 0px; 29 | padding: 0px; 30 | 31 | li { 32 | .edit { 33 | float: right; 34 | color: @color-light1; 35 | &:hover { 36 | text-decoration: none; 37 | background-color: @color-yellow; 38 | } 39 | } 40 | .remove { 41 | float: right; 42 | color: @color-light1; 43 | &:hover { 44 | text-decoration: none; 45 | background-color: @color-red; 46 | } 47 | } 48 | a { 49 | padding: 2px @padding-small; 50 | display: block; 51 | color: @color-light1; 52 | &:hover { 53 | background-color: @color-dark2; 54 | } 55 | &.active { 56 | color: @color-blue; 57 | // font-weight: 600; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/assets/less/c_notification.less: -------------------------------------------------------------------------------- 1 | @icon-size : 35px; 2 | 3 | .notification { 4 | position: fixed; 5 | bottom: 20px; 6 | right: -@size-sidebar+-@icon-size; 7 | z-index: @z-notification; 8 | opacity: 0; 9 | 10 | width: @size-sidebar+@icon-size; 11 | color: @color-dark1; 12 | transition: all 0.5s ease; 13 | 14 | &:hover { 15 | opacity: 0.7; 16 | } 17 | &.started { 18 | right: 0px; 19 | opacity: 1; 20 | } 21 | &.killed { 22 | opacity: 0; 23 | right: -@size-sidebar+-@icon-size; 24 | } 25 | 26 | &.default { 27 | background-color: @color-light4; 28 | border: 1px solid @color-light4; 29 | border-right: 0px; 30 | .notification-content { 31 | background-color: @color-light2; 32 | } 33 | } 34 | &.error { 35 | background-color: @color-red-dark; 36 | border: 1px solid @color-red-dark; 37 | border-right: 0px; 38 | .notification-content { 39 | background-color: @color-red-light; 40 | } 41 | } 42 | &.success { 43 | background-color: @color-green-dark; 44 | border: 1px solid @color-green-dark; 45 | border-right: 0px; 46 | .notification-content { 47 | background-color: @color-green-light; 48 | } 49 | } 50 | &.warning { 51 | background-color: @color-yellow-dark; 52 | border: 1px solid @color-yellow-dark; 53 | border-right: 0px; 54 | .notification-content { 55 | background-color: @color-yellow-light; 56 | } 57 | } 58 | &.info { 59 | background-color: @color-blue-dark; 60 | border: 1px solid @color-blue-dark; 61 | border-right: 0px; 62 | .notification-content { 63 | background-color: @color-blue-light; 64 | } 65 | } 66 | 67 | 68 | 69 | &-icon { 70 | color: @color-light1; 71 | position: absolute; 72 | top: 50%; 73 | left: 18px; 74 | margin-top: -15px; 75 | font-size: 1.3em; 76 | } 77 | 78 | &-content { 79 | padding: @padding-small @padding-large @padding-small @padding-large; 80 | min-height: 30px; 81 | 82 | &.has-icon { 83 | margin-left: @padding-large+@icon-size; 84 | } 85 | } 86 | 87 | &-title { 88 | font-size: 1em; 89 | font-weight: bold; 90 | margin-bottom: 5px; 91 | } 92 | 93 | &-message { 94 | font-size: 0.85em; 95 | } 96 | } -------------------------------------------------------------------------------- /src/assets/less/c_page.less: -------------------------------------------------------------------------------- 1 | 2 | .page { 3 | color: @color-dark1; 4 | overflow-y: auto; 5 | padding-bottom: 100px; 6 | min-width: 850px; 7 | 8 | h1.header { 9 | color: @color-light3; 10 | margin: 0px; 11 | overflow-y: hidden; 12 | padding: 0px; 13 | min-width: @size-sidebar+350px; 14 | padding-left: @padding-large+@size-sidebar; 15 | height: @size-header; 16 | line-height: @size-header*1.1; 17 | font-size: 2.5em; 18 | .font-header; 19 | } 20 | 21 | .content { 22 | max-width: @size-page-content; 23 | min-width: @size-sidebar+350px; 24 | width: 100%; 25 | padding: @padding-large; 26 | padding-left: @padding-large+@size-sidebar; 27 | 28 | &.no-header { padding-top: @size-header; } 29 | .current-project { margin-top: 10px; } 30 | } 31 | 32 | &-operations { 33 | background-color: @color-light1; 34 | border: 1px solid #E7E7E7; 35 | 36 | &-content { 37 | max-width: @size-page-content; 38 | min-width: @size-sidebar+350px; 39 | width: 100%; 40 | padding-right: @padding-large; 41 | padding-left: @size-sidebar; 42 | text-align: right; 43 | ul { 44 | margin: 0px; 45 | padding: 0px; 46 | list-style: none; 47 | li { 48 | display: inline; 49 | a, button { 50 | border: 0px; 51 | display: inline; 52 | // height: 22px; 53 | padding: 3.5px 13px; 54 | cursor: pointer; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/assets/less/c_properties.less: -------------------------------------------------------------------------------- 1 | .properties { 2 | padding: @padding-small-2x; 3 | 4 | input:not([type=button]), textarea { 5 | padding: @padding-small; 6 | } 7 | 8 | label { 9 | display: block; 10 | text-transform: uppercase; 11 | color: @color-light3; 12 | .font-base; 13 | } 14 | } -------------------------------------------------------------------------------- /src/assets/less/c_sidebar.less: -------------------------------------------------------------------------------- 1 | .sidebar-stub { 2 | position: absolute; 3 | top: 0px; 4 | width: @size-sidebar; 5 | height: 100%; 6 | z-index: 1; 7 | background-color: @color-dark3; 8 | } 9 | 10 | .sidebar { 11 | overflow-y: auto; 12 | position: absolute; 13 | top: 0px; 14 | width: @size-sidebar; 15 | height: 100%; 16 | z-index: @z-sidebar; 17 | color: @color-light3; 18 | background-color: @color-dark3; 19 | .box-shadow(0px 0px 16px 0px); 20 | 21 | &.left { left: 0px; } 22 | &.right { right: 0px; } 23 | &::-webkit-scrollbar { 24 | width: 3px; 25 | background: none; 26 | 27 | &-track { background: none; } 28 | &-thumb { background: @color-blue; } 29 | &-thumb:window-inactive { background: @color-blue; } 30 | } 31 | 32 | .title { 33 | color: @color-dark1; 34 | list-style: none; 35 | background-color: @color-dark2+#222; 36 | margin: @padding-medium 0px @padding-small-2x 0px; 37 | padding: 0px @padding-small-2x; 38 | text-align: left; 39 | text-transform: capitalize; 40 | font-size: 1.2em; 41 | .font-base; 42 | 43 | .new { 44 | margin-top: 3px; 45 | float: right; 46 | color: @color-light1; 47 | &:hover { 48 | text-decoration: none; 49 | background-color: @color-green; 50 | } 51 | } 52 | 53 | a { 54 | color: @color-dark1; 55 | display: block; 56 | } 57 | } 58 | 59 | .header-button { 60 | color: @color-light3; 61 | padding-left: @padding-large; 62 | height: @size-header; 63 | line-height: @size-header*1.1; 64 | font-size: 1.5em; 65 | background-color: @color-dark2; 66 | border-bottom: 1px solid @color-dark4; 67 | .font-header; 68 | 69 | a { 70 | display: block; 71 | color: @color-light2; 72 | } 73 | } 74 | 75 | .content { 76 | padding: @padding-medium 0px; 77 | 78 | &.content.no-header { padding-top: @size-header; } 79 | &.content.has-menubar { padding-top: @size-menubar; } 80 | 81 | } 82 | } 83 | 84 | .side-panel { 85 | .panel-operations { 86 | border-top: 1px solid @color-light4; 87 | border-bottom: 1px solid @color-light4; 88 | position: absolute; 89 | width:100%; 90 | bottom: 0px; 91 | background-color: @color-dark2; 92 | 93 | &-content { 94 | width: 100%; 95 | text-align: right; 96 | ul { 97 | margin: 0px; 98 | padding: 0px; 99 | list-style: none; 100 | li { 101 | display: inline; 102 | a { 103 | display: inline-block; 104 | padding: 3.5px 12px; 105 | cursor: pointer; 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /src/assets/less/c_tabset.less: -------------------------------------------------------------------------------- 1 | .tabset { 2 | height: 100%; 3 | 4 | ul.tabset-header { 5 | list-style: none; 6 | margin: 0px; 7 | padding: @padding-medium 0px; 8 | text-align: center; 9 | font-size: 1.3em; 10 | .font-header; 11 | 12 | li { 13 | display: inline; 14 | margin: 0px 10px; 15 | 16 | a { 17 | color: @color-light2; 18 | &:hover { 19 | color: @color-light1; 20 | } 21 | } 22 | 23 | &.active { 24 | a { 25 | color: @color-blue; 26 | &:hover { 27 | color: @color-blue; 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | .tab-panel { 36 | height: 100%; 37 | } -------------------------------------------------------------------------------- /src/assets/less/c_trees.less: -------------------------------------------------------------------------------- 1 | .tree-list { 2 | overflow-y: auto; 3 | height: 80%; 4 | 5 | &-content { 6 | padding: 0px @padding-small; 7 | 8 | ul { 9 | list-style: none; 10 | margin: 0px; 11 | padding: 0px; 12 | 13 | li { 14 | .remove { 15 | float: right; 16 | color: @color-light1; 17 | &:hover { 18 | text-decoration: none; 19 | background-color: @color-red; 20 | } 21 | } 22 | a { 23 | padding: 2px @padding-small; 24 | display: block; 25 | color: @color-light1; 26 | &:hover { 27 | background-color: @color-dark2; 28 | } 29 | &.active { 30 | color: @color-blue; 31 | font-weight: 600; 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | 39 | } -------------------------------------------------------------------------------- /src/assets/less/index.less: -------------------------------------------------------------------------------- 1 | @import "variables.less"; 2 | @import "bootstrap.less"; 3 | @import "animations.less"; 4 | 5 | @import "c_app.less"; 6 | @import "c_dash.less"; 7 | @import "c_keytable.less"; 8 | @import "c_menubar.less"; 9 | @import "c_modal.less"; 10 | @import "c_nodes.less"; 11 | @import "c_notification.less"; 12 | @import "c_page.less"; 13 | @import "c_properties.less"; 14 | @import "c_sidebar.less"; 15 | @import "c_tabset.less"; 16 | @import "c_trees.less"; -------------------------------------------------------------------------------- /src/assets/less/variables.less: -------------------------------------------------------------------------------- 1 | // COLORS ===================================================================== 2 | @color-dark1 : #0B0B0B; 3 | @color-dark2 : #333333; 4 | @color-dark3 : #2F2F2F; 5 | @color-dark4 : #454545; 6 | @color-light1 : #FFFFFF; 7 | @color-light2 : #CECECE; 8 | @color-light3 : #999999; 9 | @color-light4 : #545454; 10 | @color-blue : #5bc0de; 11 | @color-red : #D9534F; 12 | @color-yellow : #D58512; 13 | @color-green : #398439; 14 | @color-blue-light : lighten(@color-blue, 10%); 15 | @color-blue-dark : darken(@color-blue, 30%); 16 | @color-red-light : lighten(@color-red, 10%); 17 | @color-red-dark : darken(@color-red, 30%); 18 | @color-yellow-light : lighten(@color-yellow, 10%); 19 | @color-yellow-dark : darken(@color-yellow, 30%); 20 | @color-green-light : lighten(@color-green, 10%); 21 | @color-green-dark : darken(@color-green, 30%); 22 | 23 | // LAYER DEPTH ================================================================ 24 | @z-editor: 0; 25 | @z-page: 10; 26 | @z-sidebar: 20; 27 | @z-menubar: 30; 28 | @z-modal: 40; 29 | @z-notification: 50; 30 | 31 | // SIZES ====================================================================== 32 | @size-sidebar: 250px; 33 | @size-menubar: 35px; 34 | @size-page-content: 700px+@size-sidebar; 35 | 36 | @size-header: 100px; 37 | 38 | // PADDING ==================================================================== 39 | @padding-small: 5px; 40 | @padding-small-2x: 10px; 41 | @padding-medium: 15px; 42 | @padding-large: 25px; 43 | @padding-huge: 35px; 44 | @padding-large-2x: 25px; 45 | @padding-huge-2x: 70px; 46 | 47 | 48 | // FONTS ====================================================================== 49 | .font-base { 50 | font-family: sans-serif; 51 | font-weight: 400; 52 | } 53 | 54 | .font-header { 55 | font-family: sans-serif; 56 | font-weight: 300; 57 | } 58 | 59 | .box-shadow(@style) { 60 | -webkit-box-shadow: @style @color-dark1; 61 | box-shadow: @style @color-dark1; 62 | } 63 | 64 | // ANIMATIONS ================================================================= 65 | .animation-slow { 66 | -webkit-transition : all .50s ease-in-out; 67 | -moz-transition : all .50s ease-in-out; 68 | -o-transition : all .50s ease-in-out; 69 | transition : all .50s ease-in-out; 70 | } 71 | 72 | .animation { 73 | -webkit-transition : all .30s ease-in-out; 74 | -moz-transition : all .30s ease-in-out; 75 | -o-transition : all .30s ease-in-out; 76 | transition : all .30s ease-in-out; 77 | } 78 | 79 | .animation-fast { 80 | -webkit-transition : all .15s ease-in-out; 81 | -moz-transition : all .15s ease-in-out; 82 | -o-transition : all .15s ease-in-out; 83 | transition : all .15s ease-in-out; 84 | } -------------------------------------------------------------------------------- /src/assets/libs/mousetrap.min.js: -------------------------------------------------------------------------------- 1 | /* mousetrap v1.5.2 craig.is/killing/mice */ 2 | (function(C,r,g){function t(a,b,h){a.addEventListener?a.addEventListener(b,h,!1):a.attachEvent("on"+b,h)}function x(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return l[a.which]?l[a.which]:p[a.which]?p[a.which]:String.fromCharCode(a.which).toLowerCase()}function D(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function u(a){return"shift"==a||"ctrl"==a||"alt"==a|| 3 | "meta"==a}function y(a,b){var h,c,e,g=[];h=a;"+"===h?h=["+"]:(h=h.replace(/\+{2}/g,"+plus"),h=h.split("+"));for(e=0;em||l.hasOwnProperty(m)&&(k[l[m]]=m)}e=k[h]?"keydown":"keypress"}"keypress"==e&&g.length&&(e="keydown");return{key:c,modifiers:g,action:e}}function B(a,b){return a===r?!1:a===b?!0:B(a.parentNode,b)}function c(a){function b(a){a=a||{}; 4 | var b=!1,n;for(n in q)a[n]?b=!0:q[n]=0;b||(v=!1)}function h(a,b,n,f,c,h){var g,e,l=[],m=n.type;if(!d._callbacks[a])return[];"keyup"==m&&u(a)&&(b=[a]);for(g=0;g":".","?":"/","|":"\\"},z={option:"alt",command:"meta","return":"enter",escape:"esc", 9 | plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},k;for(g=1;20>g;++g)l[111+g]="f"+g;for(g=0;9>=g;++g)l[g+96]=g;c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};this._directMap={};return this}; 10 | c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||B(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.init=function(){var a=c(r),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};c.init();C.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=c);"function"=== 11 | typeof define&&define.amd&&define(function(){return c})})(window,document); -------------------------------------------------------------------------------- /src/desktop.js: -------------------------------------------------------------------------------- 1 | var app = require('app'); // Module to control application life. 2 | var BrowserWindow = require('browser-window'); // Module to create native browser window. 3 | 4 | // Report crashes to our server. 5 | require('crash-reporter').start(); 6 | 7 | // Keep a global reference of the window object, if you don't, the window will 8 | // be closed automatically when the JavaScript object is garbage collected. 9 | var mainWindow = null; 10 | 11 | // Quit when all windows are closed. 12 | app.on('window-all-closed', function() { 13 | // On OS X it is common for applications and their menu bar 14 | // to stay active until the user quits explicitly with Cmd + Q 15 | if (process.platform != 'darwin') { 16 | app.quit(); 17 | } 18 | }); 19 | 20 | // This method will be called when Electron has finished 21 | // initialization and is ready to create browser windows. 22 | app.on('ready', function() { 23 | // Create the browser window. 24 | mainWindow = new BrowserWindow({width: 1000, height: 800}); 25 | 26 | // and load the index.html of the app. 27 | mainWindow.loadUrl('file://' + __dirname + '/index.html'); 28 | 29 | // Open the DevTools. 30 | // mainWindow.openDevTools(); 31 | 32 | // Emitted when the window is closed. 33 | mainWindow.on('closed', function() { 34 | // Dereference the window object, usually you would store windows 35 | // in an array if your app supports multi windows, this is the time 36 | // when you should delete the corresponding element. 37 | mainWindow = null; 38 | }); 39 | }); -------------------------------------------------------------------------------- /src/editor/draw/symbols.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | 4 | b3e.draw.rootSymbol = function(block, settings) { 5 | // var shape = block.displayObject; 6 | var shape = new createjs.Shape(); 7 | 8 | var w = block._width; 9 | var h = block._height; 10 | var swidth = h/20; 11 | var ssize = h/5; 12 | var scolor = settings.get('block_symbol_color'); 13 | 14 | shape.graphics.setStrokeStyle(swidth, 'round'); 15 | shape.graphics.beginStroke(scolor); 16 | shape.graphics.drawCircle(0, 0, ssize); 17 | shape.graphics.moveTo(-ssize, ssize); 18 | shape.graphics.lineTo(ssize, -ssize); 19 | shape.graphics.endStroke(); 20 | 21 | return shape; 22 | }; 23 | 24 | b3e.draw.sequenceSymbol = function(block, settings) { 25 | // var shape = block.displayObject; 26 | // var shape = block._shapeObject; 27 | var shape = new createjs.Shape(); 28 | 29 | var w = block._width; 30 | var h = block._height; 31 | var swidth = h/20; 32 | var ssize = h/4; 33 | var scolor = settings.get('block_symbol_color'); 34 | 35 | shape.graphics.setStrokeStyle(swidth, 'round'); 36 | shape.graphics.beginStroke(scolor); 37 | shape.graphics.beginFill(scolor); 38 | shape.graphics.moveTo(-ssize, 0); 39 | shape.graphics.lineTo(ssize, 0); 40 | shape.graphics.drawPolyStar(ssize/2, 0, ssize/2, 3, 0, 0); 41 | shape.graphics.endFill(); 42 | shape.graphics.endStroke(); 43 | 44 | return shape; 45 | }; 46 | 47 | b3e.draw.memsequenceSymbol = function(block, settings) { 48 | var shape = new createjs.Shape(); 49 | 50 | var w = block._width; 51 | var h = block._height; 52 | var swidth = h/20; 53 | var ssize = h/4; 54 | var scolor = settings.get('block_symbol_color'); 55 | 56 | shape.graphics.setStrokeStyle(swidth, 'round'); 57 | shape.graphics.beginStroke(scolor); 58 | shape.graphics.beginFill(scolor); 59 | shape.graphics.drawPolyStar(0, -ssize*0.75, ssize/2, 6, ssize/10, 0); 60 | 61 | shape.graphics.setStrokeStyle(swidth, 'round'); 62 | shape.graphics.beginStroke(scolor); 63 | shape.graphics.beginFill(scolor); 64 | shape.graphics.moveTo(-ssize, ssize/2); 65 | shape.graphics.lineTo(ssize, ssize/2); 66 | shape.graphics.drawPolyStar(ssize/2, ssize/2, ssize/2, 3, 0, 0); 67 | shape.graphics.endFill(); 68 | shape.graphics.endStroke(); 69 | 70 | return shape; 71 | }; 72 | 73 | b3e.draw.prioritySymbol = function(block, settings) { 74 | // var shape = block.displayObject; 75 | // var shape = block._shapeObject; 76 | var shape = new createjs.Shape(); 77 | 78 | var w = block._width; 79 | var h = block._height; 80 | var swidth = h/20; 81 | var ssize = h/8; 82 | var scolor = settings.get('block_symbol_color'); 83 | 84 | shape.graphics.setStrokeStyle(swidth, 'round'); 85 | shape.graphics.beginStroke(scolor); 86 | shape.graphics.arc(0, -ssize, ssize, 3.141561, 1.570796, false); 87 | shape.graphics.lineTo(0, ssize); 88 | shape.graphics.beginFill(scolor); 89 | shape.graphics.drawCircle(0, ssize*2, swidth/2); 90 | 91 | shape.graphics.endFill(); 92 | shape.graphics.endStroke(); 93 | 94 | return shape; 95 | }; 96 | 97 | b3e.draw.memprioritySymbol = function(block, settings) { 98 | var shape = new createjs.Shape(); 99 | 100 | var w = block._width; 101 | var h = block._height; 102 | var swidth = h/20; 103 | var ssize = h/8; 104 | var scolor = settings.get('block_symbol_color'); 105 | 106 | shape.graphics.setStrokeStyle(swidth, 'round'); 107 | shape.graphics.beginStroke(scolor); 108 | shape.graphics.arc(-ssize, -ssize, ssize, 3.141561, 1.570796, false); 109 | shape.graphics.lineTo(-ssize, ssize); 110 | shape.graphics.beginFill(scolor); 111 | shape.graphics.drawCircle(-ssize, ssize*2, swidth/2); 112 | shape.graphics.drawPolyStar(ssize*1.5, 0, ssize/2, 6, ssize/10, 0); 113 | 114 | shape.graphics.endFill(); 115 | shape.graphics.endStroke(); 116 | 117 | return shape; 118 | }; 119 | 120 | b3e.draw.textSymbol = function(block, settings) { 121 | var text = new createjs.Text( 122 | block.getTitle(), 123 | '18px Arial', 124 | settings.get('block_symbol_color') 125 | ); 126 | text.textAlign = 'center'; 127 | 128 | var bounds = text.getBounds(); 129 | text.regY = bounds.height/2; 130 | 131 | // text.x = -block._width/2; 132 | // text.y = -block._height/2; 133 | 134 | return text; 135 | }; 136 | 137 | 138 | b3e.draw.SYMBOLS = { 139 | 'Root' : b3e.draw.rootSymbol, 140 | 'Sequence' : b3e.draw.sequenceSymbol, 141 | 'Priority' : b3e.draw.prioritySymbol, 142 | 'MemSequence' : b3e.draw.memsequenceSymbol, 143 | 'MemPriority' : b3e.draw.memprioritySymbol, 144 | }; 145 | 146 | }()); 147 | -------------------------------------------------------------------------------- /src/editor/editor/Editor.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | /** 5 | * Editor main class. 6 | */ 7 | var Editor = function() { 8 | this.Container_constructor(); 9 | 10 | // Variables 11 | this._project = null; 12 | this._game = null; 13 | this._settings = new b3e.SettingsManager(); 14 | this._dirty = 0; 15 | 16 | // Systems 17 | this._systems = []; 18 | 19 | // Managers 20 | this.project = null; 21 | this.export = null; 22 | this.import = null; 23 | this.shortcuts = null; 24 | 25 | this._createGame(); 26 | }; 27 | var p = createjs.extend(Editor, createjs.Container); 28 | 29 | p._createGame = function() { 30 | var self = this; 31 | this._game = new tine.Game(null, { 32 | update : function() { self._update(); }, 33 | }); 34 | 35 | this._initialize(); 36 | }; 37 | 38 | /** 39 | * Initializes DOM, DOM events, managers and display objects. 40 | */ 41 | p._initialize = function() { 42 | var self = this; 43 | 44 | // RESIZE 45 | var resize = function() { 46 | self._game.canvas.width = window.innerWidth; 47 | self._game.canvas.height = window.innerHeight; 48 | }; 49 | window.onresize = resize; 50 | resize(); 51 | 52 | // GAME 53 | this._game.stage.addChild(this); 54 | 55 | // MANAGERS 56 | this.project = new b3e.editor.ProjectManager(this); 57 | this.export = new b3e.editor.ExportManager(this); 58 | this.import = new b3e.editor.ImportManager(this); 59 | this.shortcuts = new b3e.editor.ShortcutManager(this); 60 | 61 | // SYSTEMS 62 | this._systems.push(new b3e.editor.CameraSystem(this)); 63 | this._systems.push(new b3e.editor.ConnectionSystem(this)); 64 | this._systems.push(new b3e.editor.SelectionSystem(this)); 65 | this._systems.push(new b3e.editor.DragSystem(this)); 66 | this._systems.push(new b3e.editor.CollapseSystem(this)); 67 | this._systems.push(new b3e.editor.ShortcutSystem(this)); 68 | 69 | // SETTINGS 70 | this.applySettings('default'); 71 | }; 72 | 73 | /** 74 | * Called by creatine game. 75 | */ 76 | p._update = function() { 77 | var delta = this._game.time.delta; 78 | this._systems.forEach(function(system) { 79 | system.update(delta); 80 | }); 81 | }; 82 | 83 | p.trigger = function(name, target, variables) { 84 | variables = variables || {}; 85 | 86 | var event = new createjs.Event(name); 87 | event._target = target; 88 | event._data = variables; 89 | this.dispatchEvent(event); 90 | }; 91 | 92 | p.applySettings = function(settings) { 93 | if (settings === 'default') { 94 | settings = b3e.DEFAULT_SETTINGS; 95 | this._settings.clear(); 96 | } 97 | 98 | if (settings) { 99 | this._settings.load(settings); 100 | } 101 | 102 | var canvas = this._game.canvas; 103 | canvas.style.backgroundColor = this._settings.get('background_color'); 104 | 105 | this.project._applySettings(this._settings); 106 | this.export._applySettings(this._settings); 107 | this.import._applySettings(this._settings); 108 | this.shortcuts._applySettings(this._settings); 109 | }; 110 | 111 | p.preview = function(name) { 112 | var canvas = document.createElement('canvas'); 113 | 114 | var p = this.project.get(); 115 | var node = p.nodes.get(name); 116 | var tree = p.trees.getSelected(); 117 | 118 | if (!node) return; 119 | var block = new b3e.Block(node); 120 | block._applySettings(this._settings); 121 | block.x = block._width; 122 | block.y = block._height; 123 | 124 | canvas.setAttribute('width', block._width*tree.scaleX*2); 125 | canvas.setAttribute('height', block._height*tree.scaleY*2); 126 | 127 | var stage = new createjs.Stage(canvas); 128 | stage.scaleX = tree.scaleX; 129 | stage.scaleY = tree.scaleY; 130 | stage.addChild(block); 131 | stage.update(); 132 | 133 | return canvas; 134 | }; 135 | 136 | p.isDirty = function() { 137 | return this._dirty !== 0; 138 | }; 139 | 140 | p.clearDirty = function() { 141 | this._dirty = 0; 142 | }; 143 | 144 | b3e.editor.Editor = createjs.promote(Editor, 'Container'); 145 | })(); -------------------------------------------------------------------------------- /src/editor/editor/managers/ExportManager.js: -------------------------------------------------------------------------------- 1 | b3e.editor.ExportManager = function(editor) { 2 | "use strict"; 3 | 4 | function getBlockChildrenIds(block) { 5 | var conns = block._outConnections.slice(0); 6 | if (editor._settings.get('layout') === 'horizontal') { 7 | conns.sort(function(a, b) { 8 | return a._outBlock.y - 9 | b._outBlock.y; 10 | }); 11 | } else { 12 | conns.sort(function(a, b) { 13 | return a._outBlock.x - 14 | b._outBlock.x; 15 | }); 16 | } 17 | 18 | var nodes = []; 19 | for (var i=0; i 0) { 92 | tree.view.zoomIn(); 93 | } else { 94 | tree.view.zoomOut(); 95 | } 96 | } 97 | }; 98 | 99 | 100 | var self = this; 101 | editor._game.stage.on('stagemousedown', this.onMouseDown, this); 102 | editor._game.stage.on('stagemousemove', this.onMouseMove, this); 103 | editor._game.stage.on('stagemouseup', this.onMouseUp, this); 104 | editor._game.canvas.addEventListener('wheel', function(e) { 105 | self.onMouseWheel(e); 106 | }, false); 107 | editor._game.canvas.addEventListener('mousewheel', function(e) { 108 | self.onMouseWheel(e); 109 | }, false); 110 | editor._game.canvas.addEventListener('DOMMouseScroll ', function(e) { 111 | self.onMouseWheel(e); 112 | }, false); 113 | }; 114 | -------------------------------------------------------------------------------- /src/editor/editor/systems/CollapseSystem.js: -------------------------------------------------------------------------------- 1 | b3e.editor.CollapseSystem = function(editor) { 2 | "use strict"; 3 | 4 | this.update = function(delta) { 5 | 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /src/editor/editor/systems/ConnectionSystem.js: -------------------------------------------------------------------------------- 1 | b3e.editor.ConnectionSystem = function(editor) { 2 | "use strict"; 3 | 4 | var connection = null; 5 | var lastOutBlock = null; 6 | 7 | this.update = function(delta) {}; 8 | 9 | this.onMouseDown = function(e) { 10 | if (e.nativeEvent.which !== 1) return; 11 | 12 | var project = editor.project.get(); 13 | if (!project) return; 14 | 15 | var tree = project.trees.getSelected(); 16 | if (!tree) return; 17 | 18 | // if clicked on block 19 | var point = tree.view.getLocalPoint(); 20 | var x = point.x; 21 | var y = point.y; 22 | var block = tree.blocks.getUnderPoint(x, y); 23 | 24 | if (connection || !block) return; 25 | 26 | if (block._hitOutAnchor(x, y)) { 27 | // if user clicked at the outAnchor 28 | connection = tree.connections.add(block, null); 29 | 30 | } else if (block._hitInAnchor(x, y)) { 31 | // if user clicked at the inAnchor 32 | var c = block._inConnection; 33 | if (!c) 34 | return; 35 | 36 | block._inConnection = null; 37 | c._outBlock = null; 38 | lastOutBlock = block; 39 | 40 | connection = c; 41 | } 42 | }; 43 | 44 | this.onMouseMove = function(e) { 45 | // if no connection, return 46 | if (!connection) return; 47 | 48 | var project = editor.project.get(); 49 | if (!project) return; 50 | 51 | var tree = project.trees.getSelected(); 52 | if (!tree) return; 53 | 54 | var point = tree.view.getLocalPoint(); 55 | var x = point.x; 56 | var y = point.y; 57 | 58 | // redraw 59 | connection._redraw(null, null, x, y); 60 | }; 61 | 62 | this.onMouseUp = function(e) { 63 | if (e.nativeEvent.which !== 1) return; 64 | 65 | // if no connection, return 66 | if (!connection) return; 67 | 68 | var project = editor.project.get(); 69 | if (!project) return; 70 | 71 | var tree = project.trees.getSelected(); 72 | if (!tree) return; 73 | 74 | 75 | var point = tree.view.getLocalPoint(); 76 | var x = point.x; 77 | var y = point.y; 78 | var block = tree.blocks.getUnderPoint(x, y); 79 | 80 | // if not connection or connection but no block 81 | project.history._beginBatch(); 82 | if (!block || block === connection._inBlock || block.category === 'root') { 83 | if (lastOutBlock) { 84 | // Add again to connection in order to create history 85 | lastOutBlock._inConnection = connection; 86 | connection._outBlock = lastOutBlock; 87 | } 88 | tree.connections.remove(connection); 89 | } else { 90 | var c; 91 | 92 | // if double parent on node 93 | if (block._inConnection) { 94 | 95 | c = block._inConnection; 96 | tree.connections.remove(c); 97 | } 98 | 99 | // if double children on root 100 | if ((connection._inBlock.category === 'root' || 101 | connection._inBlock.category === 'decorator') && 102 | connection._inBlock._outConnections.length > 1) { 103 | 104 | c = connection._inBlock._outConnections[0]; 105 | tree.connections.remove(c); 106 | } 107 | 108 | connection._outBlock = block; 109 | block._inConnection = connection; 110 | 111 | var _old = [tree.connections, tree.connections._remove, [block]]; 112 | var _new = [tree.connections, tree.connections.add, [connection._inBlock, block]]; 113 | project.history._add(new b3e.Command(_old, _new)); 114 | 115 | connection._redraw(); 116 | } 117 | project.history._endBatch(); 118 | 119 | connection = null; 120 | }; 121 | 122 | editor._game.stage.on('stagemousedown', this.onMouseDown, this); 123 | editor._game.stage.on('stagemousemove', this.onMouseMove, this); 124 | editor._game.stage.on('stagemouseup', this.onMouseUp, this); 125 | }; 126 | -------------------------------------------------------------------------------- /src/editor/editor/systems/DragSystem.js: -------------------------------------------------------------------------------- 1 | b3e.editor.DragSystem = function(editor) { 2 | "use strict"; 3 | 4 | var isDragging = false; 5 | var dragX0 = 0; 6 | var dragY0 = 0; 7 | 8 | this.update = function(delta) {}; 9 | 10 | this.onMouseDown = function(e) { 11 | if (e.nativeEvent.which !== 1 || 12 | e.nativeEvent.ctrlKey || 13 | isDragging) return; 14 | 15 | var project = editor.project.get(); 16 | if (!project) return; 17 | 18 | var tree = project.trees.getSelected(); 19 | if (!tree) return; 20 | 21 | var point = tree.view.getLocalPoint(); 22 | var x = point.x; 23 | var y = point.y; 24 | var block = tree.blocks.getUnderPoint(x, y); 25 | 26 | // if mouse not on block 27 | if (!block) return; 28 | 29 | // if no block selected 30 | if (!block._isSelected) return; 31 | 32 | // if mouse in anchor 33 | if (!block._hitBody(x, y)) return; 34 | 35 | // start dragging 36 | isDragging = true; 37 | dragX0 = x; 38 | dragY0 = y; 39 | 40 | for (var i=0; i0; 38 | }; 39 | this.canRedo = function() { 40 | return index 0) return; 50 | 51 | // Crear all after index 52 | if (queue.length > index) { 53 | queue.splice(index, queue.length-index); 54 | } 55 | 56 | // Add instruction 57 | if (batchRequests > 0) { 58 | commandBuffer.push(command); 59 | } else { 60 | index++; 61 | command.context = project.trees.getSelected(); 62 | queue.push(command); 63 | 64 | if (editor._dirty < 0) editor._dirty = 0; 65 | editor._dirty++; 66 | } 67 | 68 | // Clear excess 69 | var max = editor._settings.get('max_history'); 70 | if (queue.length > max) { 71 | queue.splice(0, 1); 72 | } 73 | }; 74 | 75 | /** 76 | * Lock the manager, so it can't receive more commands. 77 | */ 78 | this._lock = function() { 79 | // if (lockRequests===0) console.log('------- LOCK -------'); 80 | lockRequests++; 81 | }; 82 | this._unlock = function() { 83 | lockRequests--; 84 | // if (lockRequests===0) console.log('------- UNLOCK -------'); 85 | }; 86 | 87 | /** 88 | * While in batch, merges all added commands to a single command 89 | */ 90 | this._beginBatch = function() { 91 | batchRequests++; 92 | }; 93 | this._endBatch = function() { 94 | batchRequests = Math.max(0, batchRequests-1); 95 | 96 | if (batchRequests === 0) { 97 | if (commandBuffer.length > 0) { 98 | var command = new b3e.Commands(commandBuffer); 99 | command.context = project.trees.getSelected(); 100 | this._add(command); 101 | } 102 | commandBuffer = []; 103 | } 104 | }; 105 | 106 | 107 | this._applySettings = function(settings) { 108 | var max = settings.get('max_history'); 109 | if (queue.length > max) { 110 | queue.splice(0, queue.length-max); 111 | } 112 | }; 113 | }; -------------------------------------------------------------------------------- /src/editor/project/managers/NodeManager.js: -------------------------------------------------------------------------------- 1 | b3e.project.NodeManager = function(editor, project) { 2 | "use strict"; 3 | 4 | /** 5 | * Register a node to the node list. You can provide: 6 | * 7 | * - a `b3.BaseNode` instance. 8 | * - a `b3e.Node` instance. 9 | * - a generic object containing the node prototype. 10 | */ 11 | this.add = function(node, isDefault) { 12 | if (node.prototype) node = node.prototype; 13 | 14 | if (project._nodes[node.name]) { 15 | return false; 16 | } 17 | 18 | if (!(node instanceof b3e.Node)) { 19 | var n = new b3e.Node(isDefault); 20 | n.name = node.name; 21 | n.category = node.category; 22 | n.title = node.title; 23 | n.description = node.description; 24 | n.properties = tine.merge({}, node.properties||node.parameters); 25 | 26 | node = n; 27 | } 28 | 29 | project._nodes[node.name] = node; 30 | if (isDefault !== true) editor.trigger('nodeadded', node); 31 | 32 | var _old = [this, this.remove, [node]]; 33 | var _new = [this, this.add, [node]]; 34 | project.history._add(new b3e.Command(_old, _new)); 35 | 36 | return node; 37 | }; 38 | 39 | /** 40 | * 41 | */ 42 | this.get = function(node) { 43 | if (typeof node !== 'string') return node; 44 | return project._nodes[node]; 45 | }; 46 | 47 | /** 48 | * 49 | */ 50 | this.update = function(node, template) { 51 | node = this.get(node); 52 | var oldName = node.name; 53 | 54 | delete project._nodes[node.name]; 55 | 56 | if (node.name !== template.name && this.get(template.name)) return false; 57 | 58 | 59 | var _oldValues = { 60 | name : node.name, 61 | title : node.title, 62 | description : node.description, 63 | category : node.category, 64 | properties : node.properties, 65 | }; 66 | 67 | if (typeof template.name !== 'undefined') { 68 | node.name = template.name; 69 | } 70 | if (typeof template.title !== 'undefined') { 71 | node.title = template.title; 72 | } 73 | if (typeof template.category !== 'undefined') { 74 | node.category = template.category; 75 | } 76 | if (typeof template.description !== 'undefined') { 77 | node.description = template.description; 78 | } 79 | if (typeof template.properties !== 'undefined') { 80 | node.properties = tine.merge({}, template.properties); 81 | } 82 | 83 | var _newValues = { 84 | name : node.name, 85 | title : node.title, 86 | description : node.description, 87 | category : node.category, 88 | properties : node.properties, 89 | }; 90 | 91 | project.history._beginBatch(); 92 | 93 | project.trees.each(function(tree) { 94 | var blocks = tree.blocks.getAll(); 95 | for (var i=blocks.length-1; i>=0; i--) { 96 | if (blocks[i].name === oldName) { 97 | tree.blocks.update(blocks[i]); 98 | } 99 | } 100 | }); 101 | 102 | project._nodes[node.name] = node; 103 | 104 | var _old = [this, this.update, [node, _oldValues]]; 105 | var _new = [this, this.update, [node, _newValues]]; 106 | project.history._add(new b3e.Command(_old, _new)); 107 | project.history._endBatch(); 108 | 109 | editor.trigger('nodechanged', node); 110 | }; 111 | 112 | /** 113 | * 114 | */ 115 | this.remove = function(node) { 116 | project.history._beginBatch(); 117 | 118 | var name = node.name||node; 119 | delete project._nodes[name]; 120 | 121 | project.trees.each(function(tree) { 122 | var blocks = tree.blocks.getAll(); 123 | for (var i=blocks.length-1; i>=0; i--) { 124 | if (blocks[i].name === name) { 125 | tree.blocks.remove(blocks[i]); 126 | } 127 | } 128 | }); 129 | 130 | var _old = [this, this.add, [node]]; 131 | var _new = [this, this.remove, [node]]; 132 | project.history._add(new b3e.Command(_old, _new)); 133 | 134 | project.history._endBatch(); 135 | 136 | editor.trigger('noderemoved', node); 137 | }; 138 | 139 | /** 140 | * Iterates over node list. 141 | */ 142 | this.each = function(callback, thisarg) { 143 | Object.keys(project._nodes).forEach(function(key) { 144 | callback.call(thisarg, project._nodes[key]); 145 | }); 146 | }; 147 | 148 | this._applySettings = function(settings) {}; 149 | }; -------------------------------------------------------------------------------- /src/editor/project/managers/TreeManager.js: -------------------------------------------------------------------------------- 1 | b3e.project.TreeManager = function(editor, project) { 2 | "use strict"; 3 | 4 | /** 5 | * Adds a new tree to the project. 6 | */ 7 | this.add = function(_id) { 8 | var tree; 9 | 10 | if (_id instanceof b3e.tree.Tree) { 11 | tree = _id; 12 | project.addChild(tree); 13 | editor.trigger('treeadded', tree); 14 | this.select(tree); 15 | 16 | } else { 17 | project.history._beginBatch(); 18 | tree = new b3e.tree.Tree(editor, project); 19 | var root = tree.blocks.getRoot(); 20 | project.addChild(tree); 21 | editor.trigger('treeadded', tree); 22 | 23 | if (_id) tree._id = _id; 24 | 25 | var node = { 26 | name : tree._id, 27 | title : root.title, 28 | category : 'tree', 29 | }; 30 | project.nodes.add(node, true); 31 | 32 | // select if this is the only tree 33 | this.select(tree); 34 | 35 | 36 | var _old = [this, this.remove, [tree]]; 37 | var _new = [this, this.add, [tree]]; 38 | project.history._add(new b3e.Command(_old, _new)); 39 | project.history._endBatch(); 40 | } 41 | 42 | return tree; 43 | }; 44 | 45 | /** 46 | * Gets a tree by id. 47 | */ 48 | this.get = function(tree) { 49 | if (typeof tree === 'string') { 50 | for (var i=0; i=0; i--) { 55 | var block = tree._selectedBlocks[i]; 56 | 57 | if (block.category != 'root') { 58 | tree.blocks.remove(tree._selectedBlocks[i]); 59 | } 60 | } 61 | project.history._endBatch(); 62 | tree._selectedBlocks = []; 63 | 64 | console.log(project._clipboard); 65 | }; 66 | 67 | this.paste = function() { 68 | if (project._clipboard === null) return; 69 | 70 | var i; 71 | var table = {}; 72 | var blocks = []; 73 | 74 | project.history._beginBatch(); 75 | 76 | // copy blocks 77 | for (var key in project._clipboard.blocks) { 78 | var spec = project._clipboard.blocks[key]; 79 | var block = new b3e.Block(spec); 80 | 81 | spec.x += 50; 82 | spec.y += 50; 83 | block._applySettings(spec._settings); 84 | block.x = spec.x; 85 | block.y = spec.y; 86 | 87 | tree.blocks.add(block); 88 | table[key] = block; 89 | blocks.push(block); 90 | } 91 | 92 | // copy connections 93 | for (i=0; i=0; i--) { 122 | if (tree._selectedBlocks[i].category === 'root') { 123 | root = tree._selectedBlocks[i]; 124 | } else { 125 | tree.blocks.remove(tree._selectedBlocks[i]); 126 | } 127 | } 128 | 129 | // tree.selection.deselectAll(); 130 | // if (root) { 131 | // tree.selection.select(root); 132 | // } 133 | project.history._endBatch(); 134 | }; 135 | 136 | this.removeConnections = function() { 137 | project.history._beginBatch(); 138 | for (var i=0; i 0) { 146 | for (var j=block._outConnections.length-1; j>=0; j--) { 147 | tree.connections.remove(block._outConnections[j]); 148 | } 149 | } 150 | } 151 | project.history._endBatch(); 152 | }; 153 | 154 | this.removeInConnections = function() { 155 | project.history._beginBatch(); 156 | for (var i=0; i 0) { 172 | for (var j=block._outConnections.length-1; j>=0; j--) { 173 | tree.connections.remove(block._outConnections[j]); 174 | } 175 | } 176 | } 177 | project.history._endBatch(); 178 | }; 179 | 180 | this._applySettings = function(settings) { 181 | }; 182 | }; 183 | -------------------------------------------------------------------------------- /src/editor/tree/managers/OrganizeManager.js: -------------------------------------------------------------------------------- 1 | b3e.tree.OrganizeManager = function(editor, project, tree) { 2 | "use strict"; 3 | 4 | var lastLayout = null; 5 | var depth = 0; 6 | var leafCount = 0; 7 | var horizontalSpacing = 208; 8 | var verticalSpacing = 88; 9 | var verticalCompensation = 42; 10 | var orderByIndex = false; 11 | var connections = []; // to redraw connections 12 | var blocks = []; // to reposition blocks 13 | 14 | function stepH(block) { 15 | var x, y; 16 | blocks.push(block); 17 | 18 | // leaf 19 | if (block._outConnections.length === 0) { 20 | leafCount++; 21 | 22 | // leaf nodes have the position accord. to the depth and leaf cont. 23 | x = depth*horizontalSpacing; 24 | y = leafCount*verticalSpacing; 25 | } 26 | 27 | // internal node 28 | else { 29 | // internal nodes have the position acord. to the depth and the 30 | // mean position of its children 31 | var ySum = 0; 32 | var conns; 33 | 34 | if (orderByIndex) { 35 | conns = block._outConnections; 36 | } else { 37 | // get connections ordered by y position 38 | conns = block._outConnections.slice(0); 39 | conns.sort(function(a, b) { 40 | return a._outBlock.y - b._outBlock.y; 41 | }); 42 | } 43 | 44 | for (var i=0; i=0; i--) { 30 | this.deselect(tree._selectedBlocks[i]); 31 | } 32 | }; 33 | 34 | this.invertSelection = function(block) { 35 | var blocks = (block)?[block]:tree.blocks.getAll(); 36 | 37 | blocks.forEach(function(block) { 38 | if (block._isSelected) { 39 | this.deselect(block); 40 | } else { 41 | this.select(block); 42 | } 43 | }, this); 44 | }; 45 | 46 | this.selectSubtree = function(block) { 47 | var blocks = (block)?[block]:tree._selectedBlocks; 48 | var fSelect = function(block) { 49 | blocks.remove(block); 50 | this.select(block); 51 | }; 52 | 53 | while (blocks.length > 0) { 54 | blocks.pop().traversal(fSelect, this); 55 | } 56 | }; 57 | 58 | this.deselectSubtree = function(block) { 59 | var blocks = (block)?[block]:tree._selectedBlocks; 60 | 61 | var fDeselect = function(block) { 62 | blocks.remove(block); 63 | this.deselect(block); 64 | }; 65 | 66 | while (blocks.length > 0) { 67 | blocks.pop().traversal(fDeselect, this); 68 | } 69 | }; 70 | 71 | this._applySettings = function(settings) {}; 72 | 73 | }; 74 | -------------------------------------------------------------------------------- /src/editor/tree/managers/ViewManager.js: -------------------------------------------------------------------------------- 1 | b3e.tree.ViewManager = function(editor, project, tree) { 2 | "use strict"; 3 | 4 | this.reset = function() { 5 | tree.x = 0; 6 | tree.y = 0; 7 | tree.scaleX = 1; 8 | tree.scaleY = 1; 9 | }; 10 | this.zoom = function(factor) { 11 | tree.scaleX = factor; 12 | tree.scaleY = factor; 13 | }; 14 | this.zoomIn = function() { 15 | var min = editor._settings.get('zoom_min'); 16 | var max = editor._settings.get('zoom_max'); 17 | var step = editor._settings.get('zoom_step'); 18 | 19 | var zoom = tree.scaleX; 20 | this.zoom(tine.clip(zoom+step, min, max)); 21 | }; 22 | this.zoomOut = function() { 23 | var min = editor._settings.get('zoom_min'); 24 | var max = editor._settings.get('zoom_max'); 25 | var step = editor._settings.get('zoom_step'); 26 | 27 | var zoom = tree.scaleX; 28 | this.zoom(tine.clip(zoom-step, min, max)); 29 | }; 30 | this.pan = function(dx, dy) { 31 | tree.x += dx; 32 | tree.y += dy; 33 | }; 34 | this.setCam = function(x, y) { 35 | tree.x = x; 36 | tree.y = y; 37 | }; 38 | this.center = function() { 39 | var canvas = editor._game.canvas; 40 | var hw = canvas.width/2; 41 | var hh = canvas.height/2; 42 | this.setCam(hw, hh); 43 | }; 44 | this.getLocalPoint = function(x, y) { 45 | if (typeof x == 'undefined') x = editor._game.mouse.x; 46 | if (typeof y == 'undefined') y = editor._game.mouse.y; 47 | return tree.globalToLocal(x, y); 48 | }; 49 | 50 | this._applySettings = function(settings) {}; 51 | }; 52 | -------------------------------------------------------------------------------- /src/editor/utils/Command.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | /** 7 | * Represents a command for the history manager. Each command must have an 8 | * undo and a redo specification. The specification must have the following 9 | * format: 10 | * 11 | * var spec = [thisarg, methodOrFunction, params] 12 | * 13 | * For example: 14 | * 15 | * var undo = [this, this.add, [block]]; 16 | * var redo = [this, this.remove, [block]]; 17 | * var command = new b3e.Command(undo, redo); 18 | * 19 | * @class Command 20 | * @param {Array} undo The undo specification. 21 | * @param {Array} redo The redo specification. 22 | * @constructor 23 | */ 24 | b3e.Command = function(undo, redo) { 25 | 26 | if (undo.length !== 3) throw 'Invalid undo command, must have [target, method, args]'; 27 | if (redo.length !== 3) throw 'Invalid redo command, must have [target, method, args]'; 28 | 29 | function execute(target, method, args) { 30 | method.apply(target, args); 31 | } 32 | 33 | /** 34 | * The tree that is selected in the moment of command is added to the 35 | * history manager. This is set by the manager. 36 | * 37 | * @property {b3e.tree.Tree} context; 38 | */ 39 | this.context = null; 40 | 41 | /** 42 | * Execute the redo command. 43 | * 44 | * @method redo 45 | */ 46 | this.redo = function() { 47 | execute(redo[0], redo[1], redo[2]); 48 | }; 49 | 50 | /** 51 | * Execute the undo command. 52 | * 53 | * @method undo 54 | */ 55 | this.undo = function() { 56 | execute(undo[0], undo[1], undo[2]); 57 | }; 58 | }; 59 | 60 | /** 61 | * A list of commands created by the history manager. 62 | * 63 | * @class Command 64 | * @param {Array} undo The undo specification. 65 | * @param {Array} redo The redo specification. 66 | * @constructor 67 | */ 68 | b3e.Commands = function(commands) { 69 | 70 | /** 71 | * The tree that is selected in the moment of command is added to the 72 | * history manager. This is set by the manager. 73 | * 74 | * @property {b3e.tree.Tree} context; 75 | */ 76 | this.context = null; 77 | 78 | /** 79 | * Execute the redo command. 80 | * 81 | * @method redo 82 | */ 83 | this.redo = function() { 84 | for (var i=0; i=0; i--) { 96 | commands[i].undo(); 97 | } 98 | }; 99 | }; 100 | })(); -------------------------------------------------------------------------------- /src/editor/utils/Connection.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | 3 | (function () { 4 | "use strict"; 5 | 6 | /** 7 | * Represents a connection between two blocks. 8 | * 9 | * @class Connection 10 | * @constructor 11 | */ 12 | var Connection = function() { 13 | this.Shape_constructor(); 14 | 15 | this._settings = null; 16 | this._inBlock = null; 17 | this._outBlock = null; 18 | }; 19 | var p = createjs.extend(Connection, createjs.Shape); 20 | 21 | /** 22 | * Apply the editor settings to this connection. 23 | * 24 | * @method _applySettings 25 | * @param Object {b3e.SettingsManager} The settings object. 26 | * @protected 27 | */ 28 | p._applySettings = function(settings) { 29 | this._settings = settings; 30 | this._redraw(); 31 | }; 32 | 33 | /** 34 | * Redraw the connection. 35 | * 36 | * @method _redraw 37 | * @protected 38 | */ 39 | p._redraw = function(x1, y1, x2, y2) { 40 | if (! ((this._inBlock||x1||y1) && (this._outBlock||x2||y2)) ) { 41 | return; 42 | } 43 | 44 | var s = this._settings; 45 | var graphics = this.graphics; 46 | var width = s.get('connection_width'); 47 | var color = s.get('connection_color'); 48 | var diff = s.get('anchor_radius') + s.get('anchor_border_width'); 49 | var arrowWidth = s.get('anchor_radius')/2; 50 | var layout = s.get('layout'); 51 | 52 | var dx=0; var dy=0; var angle=0; var ax=0; var ay=0; 53 | // var inAnchor = this._outBlock._getInAnchorPosition(); 54 | // var outAnchor = this._inBlock._getOutAnchorPosition(); 55 | 56 | if (!(x1 === 0||x1)) { 57 | var outAnchor = this._inBlock._getOutAnchorPosition(); 58 | if (layout === 'horizontal') { 59 | x1 = outAnchor.x; 60 | y1 = this._inBlock.y; 61 | } else { 62 | x1 = this._inBlock.x; 63 | y1 = outAnchor.y; 64 | } 65 | } 66 | 67 | if (!(x2 === 0||x2)) { 68 | var inAnchor = this._outBlock._getInAnchorPosition(); 69 | if (layout === 'horizontal') { 70 | x2 = inAnchor.x - diff; 71 | y2 = this._outBlock.y; 72 | } else { 73 | x2 = this._outBlock.x; 74 | y2 = inAnchor.y - diff; 75 | } 76 | } 77 | 78 | if (layout === 'horizontal') { 79 | dx = 2.5*(x2 - x1)/4; 80 | ax = -arrowWidth; 81 | } else { 82 | dy = 2.5*(y2 - y1)/4; 83 | ay = -arrowWidth; 84 | angle = 90; 85 | } 86 | 87 | graphics.clear(); 88 | graphics.setStrokeStyle(width, 'round'); 89 | graphics.beginStroke(color); 90 | graphics.moveTo(x1, y1); 91 | graphics.bezierCurveTo(x1+dx, y1+dy, x2-dx, y2-dy, x2, y2); 92 | graphics.beginFill(color); 93 | graphics.drawPolyStar(x2+ax, y2+ay, arrowWidth, 3, 0, angle); 94 | graphics.endFill(); 95 | graphics.endStroke(); 96 | }; 97 | 98 | b3e.Connection = createjs.promote(Connection, 'Shape'); 99 | })(); -------------------------------------------------------------------------------- /src/editor/utils/EditorError.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/behavior3/behavior3editor/20c2202c7ca9616dbbd5146f87f3f8df3d3567f5/src/editor/utils/EditorError.js -------------------------------------------------------------------------------- /src/editor/utils/Node.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | (function() { 3 | 'use strict'; 4 | 5 | /** 6 | * A node specification. 7 | * 8 | * @class Node 9 | * @param {Boolean} isDefault Whether the node is provided by default or not. 10 | * @constructor 11 | */ 12 | b3e.Node = function(isDefault) { 13 | this.spec = null; 14 | this.name = null; 15 | this.title = null; 16 | this.category = null; 17 | this.description = null; 18 | this.properties = {}; 19 | this.isDefault = !!isDefault; 20 | 21 | /** 22 | * Copy this node. 23 | * 24 | * @method copy 25 | * @returns {b3e.Node} A copy of this node 26 | */ 27 | this.copy = function() { 28 | var n = new b3e.Node(this.isDefault); 29 | n.spec = this.spec; 30 | n.name = this.name; 31 | n.title = this.title; 32 | n.category = this.category; 33 | n.description = this.description; 34 | n.properties = this.properties; 35 | 36 | return n; 37 | }; 38 | }; 39 | })(); -------------------------------------------------------------------------------- /src/editor/utils/Root.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Root node specification. 3 | */ 4 | b3e.Root = { 5 | name : 'Root', 6 | category : 'root', 7 | title : 'A behavior tree' 8 | }; -------------------------------------------------------------------------------- /src/editor/utils/SelectionBox.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | /** 5 | * A selecion box in the screen. 6 | * 7 | * @class SelectionBox 8 | * @constructor 9 | */ 10 | var SelectionBox = function() { 11 | this.Shape_constructor(); 12 | 13 | this._settings = null; 14 | this.alpha = 0.3; 15 | this.visible = false; 16 | }; 17 | var p = createjs.extend(SelectionBox, createjs.Shape); 18 | 19 | /** 20 | * Apply the editor settings to the selection box. 21 | * 22 | * @method _applySettings 23 | * @param Object {b3e.SettingsManager} The settings object. 24 | * @protected 25 | */ 26 | p._applySettings = function(settings) { 27 | this._settings = settings; 28 | this._redraw(); 29 | }; 30 | 31 | /** 32 | * Redraw the box. 33 | * 34 | * @method _redraw 35 | * @protected 36 | */ 37 | p._redraw = function(x1, y1, x2, y2) { 38 | var color = this._settings.get('selection_color'); 39 | var graphics = this.graphics; 40 | 41 | var x = Math.min(x1, x2); 42 | var y = Math.min(y1, y2); 43 | var w = Math.abs(x1 -x2); 44 | var h = Math.abs(y1 -y2); 45 | 46 | graphics.clear(); 47 | graphics.beginFill(color); 48 | graphics.drawRect(x, y, w, h); 49 | graphics.endFill(); 50 | }; 51 | 52 | b3e.SelectionBox = createjs.promote(SelectionBox, 'Shape'); 53 | })(); -------------------------------------------------------------------------------- /src/editor/utils/SettingsManager.js: -------------------------------------------------------------------------------- 1 | /** @module b3e */ 2 | 3 | (function () { 4 | "use strict"; 5 | 6 | /** 7 | * The settings manager handle the editor settings. 8 | * 9 | * @class SettingsManager 10 | * @constructor 11 | */ 12 | var SettingsManager = function() { 13 | this._dict = {}; 14 | }; 15 | var p = SettingsManager.prototype; 16 | 17 | /** 18 | * Erases all current settings. 19 | * 20 | * @method clear 21 | */ 22 | p.clear = function() { 23 | this._dict = {}; 24 | }; 25 | 26 | /** 27 | * Stores a value into the settings. 28 | * 29 | * @method set 30 | * @param {String} key The unique identifier. 31 | * @param {Object} value The value. 32 | */ 33 | p.set = function(key, value) { 34 | this._dict[key] = value; 35 | }; 36 | 37 | /** 38 | * Retrieves a value from the settings. 39 | * 40 | * @method get 41 | * @param {String} key The unique identifier. 42 | * @return {Object} The value. 43 | */ 44 | p.get = function(key) { 45 | return this._dict[key]; 46 | }; 47 | 48 | /** 49 | * Stores a set of values into the settings. 50 | * 51 | * @method load 52 | * @param {String} data An object containing pairs of `key, values`. 53 | */ 54 | p.load = function(data) { 55 | for (var key in data) { 56 | this.set(key, data[key]); 57 | } 58 | }; 59 | 60 | b3e.SettingsManager = SettingsManager; 61 | })(); -------------------------------------------------------------------------------- /src/editor/utils/functions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Polyfill and util functions. 3 | */ 4 | 5 | /** 6 | * JSON 7 | */ 8 | if (!JSON3) { 9 | JSON3 = JSON; 10 | } 11 | 12 | /** 13 | * String.format function 14 | */ 15 | if (!String.prototype.format) { 16 | String.prototype.format = function() { 17 | var args = arguments; 18 | return this.replace(/{(\d+)}/g, function(match, number) { 19 | return typeof args[number] != 'undefined'? 20 | args[number] : match 21 | ; 22 | }); 23 | }; 24 | } 25 | 26 | /** 27 | * Remove function 28 | */ 29 | if (!Array.prototype.remove) { 30 | Array.prototype.remove = function() { 31 | var what, a = arguments, L = a.length, ax; 32 | while (L && this.length) { 33 | what = a[--L]; 34 | while ((ax = this.indexOf(what)) !== -1) { 35 | this.splice(ax, 1); 36 | } 37 | } 38 | return this; 39 | }; 40 | } 41 | 42 | /** 43 | * Array.forEach function 44 | * 45 | * Production steps of ECMA-262, Edition 5, 15.4.4.18 46 | * Reference: http://es5.github.io/#x15.4.4.18 47 | */ 48 | if (!Array.prototype.forEach) { 49 | Array.prototype.forEach = function(callback, thisArg) { 50 | 51 | var T, k; 52 | 53 | if (this === null) { 54 | throw new TypeError(' this is null or not defined'); 55 | } 56 | 57 | // 1. Let O be the result of calling ToObject passing the |this| value as the argument. 58 | var O = Object(this); 59 | 60 | // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". 61 | // 3. Let len be ToUint32(lenValue). 62 | var len = O.length >>> 0; 63 | 64 | // 4. If IsCallable(callback) is false, throw a TypeError exception. 65 | // See: http://es5.github.com/#x9.11 66 | if (typeof callback !== "function") { 67 | throw new TypeError(callback + ' is not a function'); 68 | } 69 | 70 | // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. 71 | if (arguments.length > 1) { 72 | T = thisArg; 73 | } 74 | 75 | // 6. Let k be 0 76 | k = 0; 77 | 78 | // 7. Repeat, while k < len 79 | while (k < len) { 80 | 81 | var kValue; 82 | 83 | // a. Let Pk be ToString(k). 84 | // This is implicit for LHS operands of the in operator 85 | // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. 86 | // This step can be combined with c 87 | // c. If kPresent is true, then 88 | if (k in O) { 89 | 90 | // i. Let kValue be the result of calling the Get internal method of O with argument Pk. 91 | kValue = O[k]; 92 | 93 | // ii. Call the Call internal method of callback with T as the this value and 94 | // argument list containing kValue, k, and O. 95 | callback.call(T, kValue, k, O); 96 | } 97 | // d. Increase k by 1. 98 | k++; 99 | } 100 | // 8. return undefined 101 | }; 102 | } 103 | 104 | /** 105 | * Object.keys function 106 | * From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 107 | */ 108 | if (!Object.keys) { 109 | Object.keys = (function() { 110 | 'use strict'; 111 | var hasOwnProperty = Object.prototype.hasOwnProperty, 112 | hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'), 113 | dontEnums = [ 114 | 'toString', 115 | 'toLocaleString', 116 | 'valueOf', 117 | 'hasOwnProperty', 118 | 'isPrototypeOf', 119 | 'propertyIsEnumerable', 120 | 'constructor' 121 | ], 122 | dontEnumsLength = dontEnums.length; 123 | 124 | return function(obj) { 125 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { 126 | throw new TypeError('Object.keys called on non-object'); 127 | } 128 | 129 | var result = [], prop, i; 130 | 131 | for (prop in obj) { 132 | if (hasOwnProperty.call(obj, prop)) { 133 | result.push(prop); 134 | } 135 | } 136 | 137 | if (hasDontEnumBug) { 138 | for (i = 0; i < dontEnumsLength; i++) { 139 | if (hasOwnProperty.call(obj, dontEnums[i])) { 140 | result.push(dontEnums[i]); 141 | } 142 | } 143 | } 144 | return result; 145 | }; 146 | }()); 147 | } -------------------------------------------------------------------------------- /src/editor/utils/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default settings of the editor. 3 | * 4 | * @constant {Object} DEFAULT_SETTINGS 5 | * @memberOf b3e 6 | */ 7 | 8 | (function () { 9 | "use strict"; 10 | 11 | var DEFAULT_SETTINGS = { 12 | // CAMERA 13 | zoom_initial : 1.0, 14 | zoom_min : 0.25, 15 | zoom_max : 2.0, 16 | zoom_step : 0.25, 17 | 18 | // EDITOR 19 | snap_x : 12, 20 | snap_y : 12, 21 | snap_offset_x : 0, 22 | snap_offset_y : 0, 23 | layout : 'horizontal', // vertical 24 | max_history : 100, 25 | 26 | // COLORS 27 | background_color : '#171717', 28 | selection_color : '#4BB2FD', 29 | block_border_color : '#6D6D6D', 30 | block_symbol_color : '#333333', 31 | anchor_background_color : '#EFEFEF', 32 | 33 | connection_color : '#6D6D6D', 34 | root_color : '#FFFFFF', 35 | decorator_color : '#FFFFFF', 36 | composite_color : '#FFFFFF', 37 | tree_color : '#FFFFFF', 38 | action_color : '#FFFFFF', 39 | condition_color : '#FFFFFF', 40 | 41 | // CONNECTION 42 | connection_width : 2, 43 | 44 | // ANCHOR 45 | anchor_border_width : 2, 46 | anchor_radius : 7, 47 | anchor_offset_x : 4, 48 | anchor_offset_y : 0, 49 | 50 | // BLOCK 51 | block_border_width : 2, 52 | block_root_width : 40, 53 | block_root_height : 40, 54 | block_tree_width : 160, 55 | block_tree_height : 40, 56 | block_composite_width : 40, 57 | block_composite_height : 40, 58 | block_decorator_width : 60, 59 | block_decorator_height : 60, 60 | block_action_width : 160, 61 | block_action_height : 40, 62 | block_condition_width : 160, 63 | block_condition_height : 40, 64 | }; 65 | 66 | b3e.DEFAULT_SETTINGS = DEFAULT_SETTINGS; 67 | })(); -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Behavior3 Editor - v[BUILD_VERSION] - http://behavior3.com 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | Behavior3 Editor 22 | 23 | 24 | Loading... 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "behavior3editor", 3 | "version" : "[BUILD_VERSION]", 4 | "main" : "desktop.js", 5 | "window": { 6 | "title" : "Behavior3 Editor (v[BUILD_VERSION]) http://behavior3.com", 7 | "position" : "center", 8 | "toolbar" : false, 9 | "frame" : true, 10 | "width" : 1000, 11 | "height" : 800, 12 | "min_width" : 1000, 13 | "min_height" : 800 14 | } 15 | } -------------------------------------------------------------------------------- /src/start.js: -------------------------------------------------------------------------------- 1 | var editor; 2 | 3 | function startApp() { 4 | var domProgress = document.getElementById('page-preload'); 5 | 6 | editor = new b3e.editor.Editor(); 7 | angular.bootstrap(document, ['app']); 8 | } --------------------------------------------------------------------------------