├── .gitignore ├── .idea ├── .name ├── angular-form-gen.iml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── jsLibraryMappings.xml ├── libraries │ └── angular_form_gen_node_modules.xml ├── misc.xml ├── modules.xml ├── runConfigurations │ ├── Karma.xml │ ├── gulpfile_js.xml │ ├── gulpfile_js_dist.xml │ ├── gulpfile_js_sandbox.xml │ └── gulpfile_js_sandbox_copy.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── webResources.xml ├── LICENSE ├── README.md ├── bower.json ├── build-config.js ├── dist ├── angular-form-gen.css ├── angular-form-gen.css.map ├── angular-form-gen.js ├── angular-form-gen.js.map ├── angular-form-gen.min.css ├── angular-form-gen.min.js └── angular-form-gen.min.js.map ├── gulp-tasks ├── bower-task.js ├── index-task.js ├── lib │ ├── gulp-autoprefixer-map.js │ ├── gulp-bower-power.js │ ├── gulp-csswring.js │ ├── gulp-mini-filter.js │ ├── gulp-modify-content.js │ ├── gulp-rename-filename.js │ ├── gulp-sort.js │ ├── gulp-wrap-src.js │ └── ng-xml.js ├── module-tasks │ ├── clean-task.js │ ├── copy-task.js │ ├── scripts-task.js │ ├── styles-task.js │ ├── svg-task.js │ └── templates-task.js ├── modules-task.js ├── run.png ├── visual-studio.targets ├── watch.png └── watch.sh ├── gulpfile.js ├── karma.conf.js ├── package.json ├── projectFilesBackup └── .idea │ ├── runConfigurations │ ├── Karma.xml │ ├── gulpfile_js.xml │ ├── gulpfile_js_dist.xml │ └── gulpfile_js_sandbox.xml │ └── workspace.xml └── src ├── angular-form-gen ├── angular-form-gen-templates.js ├── angular-form-gen.css ├── angular-form-gen.js ├── angular-form-gen.test.js ├── common │ ├── bind-expression.js │ ├── bind-expression.test.js │ ├── fg-dropdown-input-directive.js │ ├── fg-dropdown-input.css │ ├── fg-null-form.js │ ├── fg-number-input-directive.js │ ├── fg-placeholder-directive.js │ ├── fg-utils.js │ ├── fg-utils.test.js │ ├── jsonify │ │ ├── jsonify.css │ │ ├── jsonify.js │ │ └── jsonify.ng.html │ └── tabs │ │ ├── tabs-controller.js │ │ ├── tabs-controller.test.js │ │ ├── tabs-directive.js │ │ ├── tabs-directive.test.js │ │ ├── tabs-pane-directive.js │ │ ├── tabs-pane-directive.test.js │ │ ├── tabs-pane.ng.html │ │ ├── tabs.css │ │ └── tabs.ng.html ├── drag │ ├── angular-drag-queen.js │ ├── angular-drag-queen.test.js │ ├── dq-drag-area.js │ ├── dq-drag-area.test.js │ ├── dq-drag-track.js │ ├── dq-draggable.js │ └── dq-draggable.test.js ├── edit │ ├── canvas │ │ ├── canvas-controller.js │ │ ├── canvas-directive.js │ │ ├── canvas-directive.test.js │ │ ├── canvas.css │ │ ├── canvas.ng.html │ │ └── field │ │ │ ├── field-directive.js │ │ │ ├── field.css │ │ │ ├── field.ng.html │ │ │ └── properties │ │ │ ├── options │ │ │ ├── options-controller.js │ │ │ ├── options-directive.js │ │ │ ├── options-directive.test.js │ │ │ ├── options.css │ │ │ └── options.ng.html │ │ │ ├── properties-directive.js │ │ │ ├── properties-directive.test.js │ │ │ ├── properties.css │ │ │ ├── properties.ng.html │ │ │ ├── property-field │ │ │ ├── common-directive.js │ │ │ ├── common-directive.test.js │ │ │ ├── common.ng.html │ │ │ ├── field-value-directive.js │ │ │ ├── field-value.ng.html │ │ │ ├── property-field-directive.js │ │ │ ├── property-field-directive.test.js │ │ │ └── property-field.ng.html │ │ │ └── validation │ │ │ ├── parse-pattern-directive.js │ │ │ ├── validation-directive.js │ │ │ ├── validation-message-directive.js │ │ │ ├── validation-message.ng.html │ │ │ ├── validation.css │ │ │ └── validation.ng.html │ ├── edit-controller.js │ ├── edit-controller.test.js │ ├── edit-directive.js │ ├── edit-directive.test.js │ ├── edit.css │ ├── edit.ng.html │ └── palette │ │ ├── categories │ │ ├── categories-controller.js │ │ ├── categories-controller.test.js │ │ ├── categories-directive.js │ │ ├── categories-directive.test.js │ │ └── categories.ng.html │ │ ├── palette-controller.js │ │ ├── palette-controller.test.js │ │ ├── palette-directive.js │ │ ├── palette-directive.test.js │ │ ├── palette.css │ │ └── palette.ng.html ├── field-templates │ ├── default │ │ ├── checkbox.ng.html │ │ ├── checkboxlist-directive.js │ │ ├── checkboxlist.ng.html │ │ ├── dropdownlist.ng.html │ │ ├── email.ng.html │ │ ├── not-in-cache.ng.html │ │ ├── number.ng.html │ │ ├── password.ng.html │ │ ├── radiobuttonlist.ng.html │ │ ├── selectlist-directive.js │ │ ├── selectlist.ng.html │ │ ├── text.ng.html │ │ └── textarea.ng.html │ └── properties │ │ ├── checkbox.ng.html │ │ ├── checkboxlist.ng.html │ │ ├── dropdownlist.ng.html │ │ ├── email.ng.html │ │ ├── number.ng.html │ │ ├── password.ng.html │ │ ├── radiobuttonlist.ng.html │ │ ├── selectlist.ng.html │ │ ├── text.ng.html │ │ └── textarea.ng.html ├── form │ ├── field │ │ ├── field-controller.js │ │ ├── field-controller.test.js │ │ ├── field-directive.js │ │ ├── field-directive.test.js │ │ ├── field-focus-directive.js │ │ ├── field-input-directive.js │ │ ├── field-input-directive.test.js │ │ └── field.ng.html │ ├── form-controller.js │ ├── form-controller.test.js │ ├── form-directive.js │ ├── form-directive.test.js │ ├── form-fields │ │ ├── form-fields-directive.js │ │ ├── form-fields-directive.test.js │ │ ├── form-fields.css │ │ └── form-fields.ng.html │ ├── form.css │ └── schema │ │ ├── schema-controller.js │ │ ├── schema-controller.test.js │ │ ├── schema-directive.js │ │ └── schema-directive.test.js └── validation │ ├── summary-directive.js │ ├── summary-directive.test.js │ ├── summary.css │ ├── summary.ng.html │ ├── unique-field-name.js │ └── unique-field-name.test.js ├── angular-logo ├── angular-logo-templates.js ├── angular-logo.css ├── angular-logo.js ├── angular-logo.ng.html └── angular-logo.ng.svg ├── angular-seo ├── angular-seo-templates.js ├── angular-seo.js └── angular-seo.test.js ├── app ├── app-templates.js ├── app.js ├── app.less ├── assets │ └── favicon.ico ├── core │ ├── menu-items.js │ ├── navbar │ │ ├── navbar.js │ │ ├── navbar.ng.html │ │ └── navbar.test.js │ ├── routing.js │ ├── utils.js │ └── utils.test.js ├── demo │ ├── data │ │ ├── data-list.html │ │ ├── data-list.js │ │ ├── data-resource.js │ │ ├── edit │ │ │ ├── data-edit.html │ │ │ └── data-edit.js │ │ └── form-resource.js │ ├── demo.less │ ├── edit │ │ ├── edit-btn-bar.html │ │ ├── edit.css │ │ ├── edit.html │ │ ├── edit.js │ │ ├── form-edit-schema.html │ │ ├── form-edit.html │ │ └── form-preview.html │ ├── form-breadcrumb.html │ ├── form-breadcrumb.js │ ├── form-list-ctrl.js │ ├── form-meta-info.js │ ├── index.html │ ├── routing.js │ └── table-button.js └── home │ ├── home.js │ ├── home.less │ └── index.html ├── github-logo ├── github-icon-small.ng.svg ├── github-logo-templates.js ├── github-logo.js └── github-logo.ng.svg ├── index.html ├── sandbox ├── ie11-bug-input-disabled │ ├── README.md │ ├── example.html │ ├── form-status.html │ ├── header.html │ ├── index.html │ ├── preview.html │ ├── script.js │ └── style.css └── plunker │ ├── form-preview │ ├── README.md │ ├── example.html │ ├── form-status.html │ ├── header.html │ ├── index.html │ ├── preview.html │ ├── script.js │ └── style.css │ ├── schema-edit │ ├── README.md │ ├── example.html │ ├── header.html │ ├── index.html │ ├── script.js │ └── style.css │ ├── schema-render │ ├── README.md │ ├── example.html │ ├── form-status.html │ ├── header.html │ ├── index.html │ ├── script.js │ └── style.css │ └── wizard │ ├── README.md │ ├── example.html │ ├── header.html │ ├── index.html │ ├── my-wizard.html │ ├── script.js │ └── style.css └── sitemap.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/workspace.xml 2 | .idea/tasks.xml 3 | .DS_Store 4 | node_modules/* 5 | bower_components/* 6 | dest/* 7 | *.log 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | angular-form-gen -------------------------------------------------------------------------------- /.idea/angular-form-gen.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/angular_form_gen_node_modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Karma.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulpfile_js.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulpfile_js_dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulpfile_js_sandbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gulpfile_js_sandbox_copy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/webResources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 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 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-form-gen", 3 | "description": "Dynamic form schema generator for AngularJS", 4 | "main": [ 5 | "dist/angular-form-gen.js", 6 | "dist/angular-form-gen.css" 7 | ], 8 | "license": "MIT", 9 | "homepage": "http://angular-form-gen.nullest.com", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/McNull/angular-form-gen.git" 13 | }, 14 | "keywords": [ 15 | "angularjs", 16 | "angular", 17 | "form", 18 | "schema", 19 | "generator" 20 | ], 21 | "version": "0.1.0-beta.7", 22 | "authors": [ 23 | "Null McNull https://github.com/McNull" 24 | ], 25 | "moduleType": [ 26 | "globals" 27 | ], 28 | "ignore": [ 29 | "**/.*", 30 | "node_modules", 31 | "bower_components", 32 | "gulp-tasks", 33 | "src", 34 | "dest", 35 | "/build-config.js", 36 | "/gulpfile.js", 37 | "/package.json", 38 | "/karma.conf.js" 39 | ], 40 | "dependencies": { 41 | "bootstrap": "~3.2.0", 42 | "angular": "~1.2.21" 43 | }, 44 | "devDependencies": { 45 | "angular-route": "~1.2.21", 46 | "angular-mocks": "~1.2.21", 47 | "jquery": "1.*", 48 | "respond": "~1.4.2", 49 | "html5shiv": "~3.7.2", 50 | "lodash": "~2.4.1", 51 | "angular-inform": "~0.0.12", 52 | "angular-block-ui": "~0.1.0", 53 | "angular-markdown-text": "~0.0.2", 54 | "angular-sanitize": "~1.2.21", 55 | "angular-animate": "~1.2.21" 56 | }, 57 | "overrides": { 58 | "respond": { 59 | "main": "dest/respond.min.js" 60 | }, 61 | "angular-mocks": { 62 | "ignore": true 63 | }, 64 | "lodash": { 65 | "ignore": true 66 | }, 67 | "showdown": { 68 | "main": "compressed/showdown.js" 69 | } 70 | }, 71 | "resolutions": { 72 | "angular": "~1.2.16" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /build-config.js: -------------------------------------------------------------------------------- 1 | // - - - - - 8-< - - - - - - - - - - - - - - 2 | 3 | var _ = require('lodash'); 4 | 5 | // - - - - - 8-< - - - - - - - - - - - - - - 6 | 7 | var pkg = require('./package.json'); 8 | pkg.year = pkg.year || new Date().getFullYear(); 9 | 10 | // - - - - - 8-< - - - - - - - - - - - - - - 11 | 12 | var config = { 13 | 14 | /* 'production' || 'development' */ 15 | env: process.env.NODE_ENV, 16 | 17 | folders: { 18 | dest: 'dest/', 19 | src: 'src/' 20 | }, 21 | 22 | modules: [ 23 | { 24 | name: 'angular-form-gen', 25 | alias: 'fg' 26 | }, 27 | { 28 | name: 'angular-logo', 29 | alias: 'ngLogo' 30 | }, 31 | { 32 | name: 'github-logo', 33 | alias: 'githubLogo' 34 | }, 35 | { 36 | name: 'angular-seo', 37 | alias: 'seo' 38 | }, 39 | { 40 | name: 'app' 41 | } 42 | ], 43 | bower: { 44 | includeDev: true 45 | }, 46 | header: _.template([ 47 | '/*!', 48 | ' <%= pkg.name %> v<%= pkg.version %>', 49 | ' (c) <%= pkg.year %> <%= pkg.author %> <%= pkg.homepage %>', 50 | ' License: <%= pkg.license %>', 51 | '*/\n' 52 | ].join('\n'), { pkg: pkg }) 53 | 54 | }; 55 | 56 | // - - - - - 8-< - - - - - - - - - - - - - - 57 | 58 | module.exports = config; -------------------------------------------------------------------------------- /gulp-tasks/bower-task.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 4 | 5 | var path = require('path'); 6 | var clean = require('gulp-rimraf'); 7 | 8 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 9 | 10 | var bower = require('./lib/gulp-bower-power.js'); 11 | var config = require('../build-config.js'); 12 | 13 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 14 | 15 | module.exports = function(gulp) { 16 | gulp.task('bower-clean', function () { 17 | 18 | return gulp.src(path.join(config.folders.dest, 'vendor'), { read: false }) 19 | .pipe(clean()); 20 | 21 | }); 22 | 23 | gulp.task('bower', ['bower-clean'], function () { 24 | 25 | return bower.src(gulp, { env: config.env, includeDev: config.bower && config.bower.includeDev }) 26 | .pipe(gulp.dest(path.join(config.folders.dest, 'vendor'))); 27 | 28 | }); 29 | }; 30 | 31 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 32 | -------------------------------------------------------------------------------- /gulp-tasks/index-task.js: -------------------------------------------------------------------------------- 1 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 2 | 3 | var config = require('../build-config.js'); 4 | var modify = require('./lib/gulp-modify-content.js'); 5 | var pkg = require('../package.json'); 6 | var _ = require('lodash'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | 10 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 11 | 12 | module.exports = function (gulp) { 13 | 14 | gulp.task('index', ['modules'], function () { 15 | 16 | return gulp.src('index.html', { 17 | cwd: config.folders.src 18 | }).pipe(modify(function (content) { 19 | 20 | var css = [], js = []; 21 | 22 | var ext = config.env === 'production' ? '.min' : ''; 23 | 24 | _.forEach(config.modules, function (module) { 25 | 26 | var moduleBase = module.name + '/' + module.name + ext; 27 | var moduleCss = moduleBase + '.css'; 28 | var moduleJs = moduleBase + '.js'; 29 | 30 | if(fs.existsSync(path.join(config.folders.dest, moduleCss))) { 31 | css.push(module.name + '/' + module.name + ext + '.css'); 32 | } 33 | 34 | if(fs.existsSync(path.join(config.folders.dest, moduleJs))) { 35 | js.push(module.name + '/' + module.name + ext + '.js'); 36 | } 37 | }); 38 | 39 | return _.template(content, { 40 | pkg: pkg, css: css, js: js 41 | }); 42 | 43 | })).pipe(gulp.dest(config.folders.dest)); 44 | 45 | }); 46 | }; 47 | 48 | // - - - - 8-< - - - - - - - - - - - - - - - - - - - 49 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-autoprefixer-map.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | Hack to enable source maps in with gulp-sourcemaps and autoprefixer. 5 | 6 | Created by null on 12/08/14. 7 | 8 | npm --save-dev install through2 9 | npm --save-dev install autoprefixer 10 | npm --save-dev install vinyl-sourcemaps-apply 11 | 12 | var prefixer = require('./gulp-autoprefixer-map.js'); 13 | 14 | gulp.task('css', [], function() { 15 | .pipe(sourcemaps.init()) 16 | .pipe(prefixer()) 17 | .pipe(sourcemaps.write('.')) 18 | }); 19 | 20 | */ 21 | 22 | var through = require('through2'); 23 | var prefix = require('autoprefixer'); 24 | //var applySourceMap = require('vinyl-sourcemaps-apply'); 25 | 26 | module.exports = function(browsers, options) { 27 | 28 | options = options || {}; 29 | 30 | return through.obj(function(file, enc, cb) { 31 | 32 | if(file.isStream()) { 33 | throw new Error('Streams not supported'); 34 | } 35 | 36 | if(!file.isNull()) { 37 | 38 | if(file.sourceMap) { 39 | options.map = { 40 | prev: file.sourceMap.mappings ? file.sourceMap : undefined, 41 | annotation: false, 42 | sourcesContent: true 43 | }; 44 | options.to = options.from = file.relative; 45 | } 46 | 47 | var contents = file.contents.toString(); 48 | 49 | var result = prefix.apply(this, browsers).process(contents, options); 50 | contents = result.css; 51 | 52 | file.contents = new Buffer(contents); 53 | 54 | if(file.sourceMap) { 55 | var map = JSON.parse(result.map.toString()); 56 | 57 | map.sources = file.sourceMap.sources; 58 | map.file = file.sourceMap.file; 59 | 60 | file.sourceMap = map; 61 | 62 | //applySourceMap(file, map); 63 | } 64 | } 65 | 66 | this.push(file); 67 | cb(); 68 | 69 | }); 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-bower-power.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | 6 | var mainBowerFiles = require('main-bower-files'); 7 | 8 | module.exports = { 9 | src: src 10 | }; 11 | 12 | function src(gulp, options) { 13 | 14 | options = options || {}; 15 | 16 | // Grab globs from main-bower-files module. 17 | // This sorts out the dependencies. 18 | 19 | var globs = mainBowerFiles(options); 20 | 21 | for(var i = 0; i < globs.length; i++) { 22 | 23 | var glob = globs[i]; 24 | 25 | var ext = path.extname(glob); 26 | 27 | // Check if the extension is JS or css 28 | if(ext === '.js' || ext === '.css') { 29 | 30 | if(glob.indexOf('.min') == -1) { 31 | // Add the minified version file name to the list 32 | var dirname = path.dirname(glob); 33 | var basename = path.basename(glob); 34 | 35 | var minified = basename.split('.'); 36 | minified.splice(minified.length - 1, 0, 'min'); 37 | minified = minified.join('.'); 38 | minified = path.join(dirname, minified); 39 | 40 | i += 1; 41 | globs.splice(i, 0, minified); 42 | 43 | // Add the source map file name for the minified version 44 | 45 | i += 1; 46 | globs.splice(i, 0, minified + '.map'); 47 | } 48 | 49 | // Add the source map file name for the unminified version 50 | 51 | i += 1; 52 | globs.splice(i, 0, glob + '.map'); 53 | 54 | } 55 | } 56 | 57 | return gulp.src(globs, options); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-csswring.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var through = require('through2'); 4 | var csswring = require('csswring'); 5 | var applySourceMap = require('vinyl-sourcemaps-apply'); 6 | 7 | module.exports = function(options) { 8 | 9 | options = options || {}; 10 | 11 | return through.obj(function(file, enc, cb) { 12 | 13 | if(file.isStream()) { 14 | throw new Error('Streams are not supported.'); 15 | } 16 | 17 | if (!file.isNull()) { 18 | 19 | if(file.sourceMap) { 20 | options.map = { 21 | prev: file.sourceMap.mappings ? file.sourceMap : undefined, 22 | annotation: false, 23 | sourcesContent: true 24 | }; 25 | options.from = 26 | options.to = file.relative; 27 | } 28 | 29 | var contents = file.contents.toString(); 30 | 31 | var result = csswring().wring(contents, options); 32 | 33 | file.contents = new Buffer(result.css); 34 | 35 | if(file.sourceMap) { 36 | var map = JSON.parse(result.map.toString()); 37 | 38 | map.sources = file.sourceMap.sources; 39 | map.file = file.sourceMap.file; 40 | 41 | // applySourceMap(file, map); 42 | file.sourceMap = map; 43 | } 44 | } 45 | 46 | this.push(file); 47 | cb(); 48 | 49 | }); 50 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-mini-filter.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var through = require('through2'); 4 | 5 | module.exports = function filter(fn) { 6 | return through.obj(function(file, enc, cb) { 7 | 8 | if(fn(file)) { 9 | this.push(file); 10 | } 11 | 12 | cb(); 13 | }); 14 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-modify-content.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var through = require('through2'); 4 | 5 | module.exports = function modify(modifiers) { 6 | 7 | return through.obj(function(file, encoding, done) { 8 | 9 | var stream = this; 10 | 11 | function applyModifiers(content) { 12 | (typeof modifiers === 'function' ? [modifiers] : modifiers).forEach(function(modifier) { 13 | content = modifier(content, file); 14 | }); 15 | return content; 16 | } 17 | 18 | function write(data) { 19 | file.contents = new Buffer(data); 20 | stream.push(file); 21 | return done(); 22 | } 23 | 24 | if (file.isBuffer()) { 25 | return write(applyModifiers(String(file.contents))); 26 | } else if (file.isStream()) { 27 | var buffer = ''; 28 | file.contents.on('data', function(chunk) { 29 | buffer += chunk; 30 | }); 31 | file.contents.on('end', function() { 32 | write(applyModifiers(String(buffer))); 33 | }); 34 | } 35 | }); 36 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-rename-filename.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | var through = require('through2'); 4 | 5 | function rename(filename) { 6 | return through.obj(function(file, enc, cb) { 7 | 8 | file.path = path.join(file.base, filename); 9 | 10 | if(file.sourceMap) { 11 | file.sourceMap.file = file.relative; 12 | } 13 | 14 | this.push(file); 15 | cb(); 16 | }); 17 | } 18 | 19 | module.exports = rename; -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-sort.js: -------------------------------------------------------------------------------- 1 | 2 | var through = require('through2'); 3 | 4 | module.exports = function(sortFn) { 5 | 6 | var files = []; 7 | 8 | function onFile(file, enc, cb) { 9 | files.push(file); 10 | cb(); 11 | } 12 | 13 | function onEnd(cb) { 14 | var self = this; 15 | 16 | files.sort(sortFn); 17 | 18 | files.forEach(function(file) { 19 | self.push(file); 20 | }); 21 | 22 | cb(); 23 | } 24 | 25 | return through.obj(onFile, onEnd); 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /gulp-tasks/lib/gulp-wrap-src.js: -------------------------------------------------------------------------------- 1 | var File = require('vinyl'); 2 | var through = require('through2'); 3 | 4 | // options = { 5 | // header: { 6 | // contents: '/* my header */', 7 | // path: 'my-virtual-header.js' 8 | // } || '/* my header */, 9 | // footer: { 10 | // contents: '/* my footer */', 11 | // path: 'my-virtual-footer.js' 12 | // } || '/* my footer */ 13 | // }; 14 | 15 | module.exports = function (options) { 16 | 17 | var pushedHeader = false; 18 | 19 | function pushHeader(file, enc, cb) { 20 | 21 | if (!pushedHeader) { 22 | pushedHeader = true; 23 | 24 | if (typeof(options.header) == 'string') { 25 | options.header = { 26 | contents: options.header 27 | }; 28 | } 29 | 30 | if (options.header.contents === undefined) { 31 | throw new Error('No header content provided.'); 32 | } 33 | 34 | var headerFile = new File({ 35 | path: options.header.path || 'header', 36 | contents: new Buffer(options.header.contents) 37 | }); 38 | 39 | this.push(headerFile); 40 | } 41 | 42 | this.push(file); 43 | cb(); 44 | 45 | } 46 | 47 | function pass(file, enc, cb) { 48 | this.push(file); 49 | cb(); 50 | } 51 | 52 | function pushFooter(cb) { 53 | 54 | if (typeof(options.footer) == 'string') { 55 | options.footer = { 56 | contents: options.footer 57 | }; 58 | } 59 | 60 | if (options.header.contents === undefined) { 61 | throw new Error('No header content provided.'); 62 | } 63 | 64 | var footerFile = new File({ 65 | path: options.footer.path || 'footer', 66 | contents: new Buffer(options.footer.contents) 67 | }); 68 | 69 | this.push(footerFile); 70 | 71 | cb(); 72 | } 73 | 74 | return through.obj( 75 | options.header ? pushHeader : pass, 76 | options.footer ? pushFooter : undefined 77 | ); 78 | }; -------------------------------------------------------------------------------- /gulp-tasks/lib/ng-xml.js: -------------------------------------------------------------------------------- 1 | // Optimised version of https://github.com/fraserxu/gulp-html2js 2 | 3 | var path = require('path'); 4 | var through = require('through2'); 5 | var File = require('vinyl'); 6 | 7 | var bsRegexp = new RegExp('\\\\', 'g'); 8 | var quoteRegexp = new RegExp('\\\"', 'g'); 9 | var nlReplace = '\\n\'' + ' +\n \''; 10 | 11 | function escape(content) { 12 | return content 13 | .replace(bsRegexp, '\\\\') 14 | .replace(quoteRegexp, '\\\"') 15 | .replace(/'/g, '\\\'') 16 | .replace(/\r?\n/g, nlReplace); 17 | } 18 | 19 | function templateCache(content, key) { 20 | return '$templateCache.put(\'' + key + '\', \'' + escape(content) + '\');' 21 | } 22 | 23 | function ensureSlashPath(p) { 24 | if (path.sep !== '/') { 25 | p = p.replace(/\\/g, '/') 26 | } 27 | return p; 28 | } 29 | 30 | /* 31 | options.base: base filepath of the template. This value is used as cache key. 32 | options.moduleName: the name of the module where to inject the templates. 33 | options.filename: [optional] target filename of the result. 34 | */ 35 | 36 | module.exports = function (options) { 37 | 38 | var buffer = []; 39 | 40 | function transform(file, encoding, callback) { 41 | 42 | var key = ensureSlashPath(path.relative(options.base, file.path)); 43 | var contents = templateCache(file.contents.toString(), key); 44 | 45 | buffer.push(' ' + contents); 46 | callback(); 47 | 48 | } 49 | 50 | function flush(callback) { 51 | 52 | var contents = 'angular.module(\'' + options.moduleName + '\').run([\'$templateCache\', function($templateCache){\n' + 53 | buffer.join('\n') + '\n}]);'; 54 | 55 | 56 | var file = new File({ 57 | path: options.filename || options.moduleName + '-templates.js', 58 | contents: new Buffer(contents) 59 | }); 60 | 61 | this.push(file); 62 | 63 | return callback(); 64 | } 65 | 66 | return through.obj(transform, flush); 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/clean-task.js: -------------------------------------------------------------------------------- 1 | 2 | // Not used anymore; each module-task has it's own associated clean task. 3 | 4 | //var clean = require('gulp-clean'); 5 | // 6 | //module.exports = function(gulp, module) { 7 | // 8 | // module.task('clean', function () { 9 | // 10 | // return gulp.src(module.folders.dest, { read: false }) 11 | // .pipe(clean({ force: true })); 12 | // 13 | // }); 14 | // 15 | // 16 | //}; -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/svg-task.js: -------------------------------------------------------------------------------- 1 | 2 | var svgmin = require('gulp-svgmin'); 3 | var path = require('path'); 4 | 5 | module.exports = function(gulp, module) { 6 | 7 | module.task('svg-clean', function() { 8 | 9 | var outputFiles = [ 10 | path.join(module.folders.dest, '**/*.svg') 11 | ]; 12 | 13 | var clean = require('gulp-rimraf'); 14 | 15 | return gulp.src(outputFiles, { read: false }) 16 | .pipe(clean({ force: true })); 17 | 18 | }); 19 | 20 | module.task('svg', 'svg-clean', function () { 21 | 22 | var glob = [ 23 | path.join(module.folders.src, '/**/*.svg'), 24 | '!**/*.ng.svg', 25 | '!**/*.ignore.svg' 26 | ]; 27 | 28 | return gulp.src(glob) 29 | .pipe(module.touch()) 30 | .pipe(svgmin()) 31 | .pipe(gulp.dest(module.folders.dest)); 32 | 33 | }); 34 | 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /gulp-tasks/module-tasks/templates-task.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * Created by null on 11/08/14. 4 | */ 5 | 6 | var minifyHtml = require('gulp-minify-html'); 7 | var svgmin = require('gulp-svgmin'); 8 | var merge = require('merge-stream'); 9 | 10 | var path = require('path'); 11 | 12 | var ngXml = require('../lib/ng-xml.js'); 13 | var modify = require('../lib/gulp-modify-content.js'); 14 | var config = require('../../build-config.js'); 15 | 16 | module.exports = function(gulp, module) { 17 | 18 | var inputFiles = [ 19 | path.join(module.folders.src, '**/*.ng.html'), 20 | path.join(module.folders.src, '**/*.ng.svg') 21 | ]; 22 | 23 | module.watch('templates', function() { 24 | return { 25 | glob: inputFiles, 26 | tasks: ['scripts'] 27 | }; 28 | }); 29 | 30 | module.task('templates-clean', function(cb) { 31 | 32 | cb(); 33 | // var outputFiles = [ 34 | // path.join(module.folders.dest, module.name + '-templates.js') 35 | // ]; 36 | // 37 | // var clean = require('gulp-rimraf'); 38 | // 39 | // return gulp.src(outputFiles, { read: false }) 40 | // .pipe(clean({ force: true })); 41 | 42 | }); 43 | 44 | module.task('templates', 'templates-clean', function () { 45 | 46 | var ngHtmlGlob = [ 47 | path.join(module.folders.src, '**/*.ng.html'), 48 | '!**/*.ignore.ng.html' 49 | ]; 50 | 51 | var ngSvgGlob = [ 52 | path.join(module.folders.src, '/**/*.ng.svg'), 53 | '!**/*.ignore.ng.svg' 54 | ]; 55 | 56 | var ngHtmlStream = gulp.src(ngHtmlGlob) 57 | .pipe(module.touch()) 58 | .pipe(minifyHtml({ 59 | empty: true, 60 | spare: true, 61 | quotes: true 62 | })); 63 | 64 | var ngSvgStream = gulp.src(ngSvgGlob) 65 | .pipe(module.touch()) 66 | .pipe(svgmin()); 67 | 68 | return merge(ngHtmlStream, ngSvgStream) 69 | .pipe(ngXml({ 70 | moduleName: module.alias || module.name, 71 | base: config.folders.src, 72 | filename: module.name + '-templates.js' 73 | })) 74 | .pipe(modify(function(contents) { 75 | return [ 76 | '// ATTENTION!', 77 | '// DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY', 78 | '// SO ALL YOUR CHANGES WILL BE LOST THE NEXT TIME THE FILE IS GENERATED', 79 | '' 80 | ].join('\n') + contents; 81 | })) 82 | .pipe(gulp.dest(module.folders.src)); 83 | 84 | }); 85 | 86 | }; 87 | 88 | -------------------------------------------------------------------------------- /gulp-tasks/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McNull/angular-form-gen/61096be66c7e0960a6ade1187b123550836b7025/gulp-tasks/run.png -------------------------------------------------------------------------------- /gulp-tasks/visual-studio.targets: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | release 9 | debug 10 | c:\program files (x86)\nodejs\node.exe 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /gulp-tasks/watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McNull/angular-form-gen/61096be66c7e0960a6ade1187b123550836b7025/gulp-tasks/watch.png -------------------------------------------------------------------------------- /gulp-tasks/watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # At the moment of writing gulp doesn't handle thrown exceptions very well when watching for file modifications. 4 | # This ensures the watch is up and running. 5 | 6 | # https://github.com/gulpjs/gulp/issues/216 7 | 8 | gulp build 9 | 10 | until gulp watch 11 | do growlnotify -name gulp -m "gulp watch restarted" --image watch.png 12 | sleep 1 13 | done -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 14 2014 11:05:59 GMT+0200 (CEST) 3 | 4 | var path = require('path'); 5 | var buildConfig = require('./build-config.js'); 6 | 7 | module.exports = function(config) { 8 | config.set({ 9 | 10 | // base path that will be used to resolve all patterns (eg. files, exclude) 11 | basePath: '', 12 | 13 | 14 | // frameworks to use 15 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 16 | frameworks: ['jasmine'], 17 | 18 | 19 | // list of files / patterns to load in the browser 20 | files: [ 21 | 'bower_components/jquery/dist/jquery.js', 22 | 'bower_components/angular/angular.js', 23 | 'bower_components/angular-mocks/angular-mocks.js', 24 | 'bower_components/lodash/dist/lodash.compat.js', 25 | path.join(buildConfig.folders.src, 'angular-form-gen/angular-form-gen.js'), 26 | path.join(buildConfig.folders.src, 'angular-form-gen/**/*.js'), 27 | path.join(buildConfig.folders.src, 'angular-seo/angular-seo.js'), 28 | path.join(buildConfig.folders.src, 'angular-seo/**/*.js') 29 | ], 30 | 31 | 32 | // list of files to exclude 33 | exclude: [ 34 | ], 35 | 36 | 37 | // preprocess matching files before serving them to the browser 38 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 39 | preprocessors: { 40 | }, 41 | 42 | 43 | // test results reporter to use 44 | // possible values: 'dots', 'progress' 45 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 46 | reporters: ['dots', 'growl'], 47 | 48 | 49 | // web server port 50 | port: 9876, 51 | 52 | 53 | // enable / disable colors in the output (reporters and logs) 54 | colors: true, 55 | 56 | 57 | // level of logging 58 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 59 | logLevel: config.LOG_INFO, 60 | 61 | 62 | // enable / disable watching file and executing tests whenever any file changes 63 | autoWatch: true, 64 | 65 | 66 | // start these browsers 67 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 68 | browsers: ['PhantomJS'], 69 | 70 | 71 | // Continuous Integration mode 72 | // if true, Karma captures browsers, runs the tests and exits 73 | singleRun: false 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-form-gen", 3 | "version": "0.1.0-beta.7", 4 | "description": "Dynamic form schema generator for AngularJS", 5 | "homepage": "https://github.com/McNull/angular-form-gen", 6 | "main": "gulp", 7 | "scripts": { 8 | "test": "gulp test" 9 | }, 10 | "keywords": [ 11 | "angularjs", 12 | "angular", 13 | "form", 14 | "schema", 15 | "generator" 16 | ], 17 | "author": "(null) McNull", 18 | "license": "MIT", 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "autoprefixer": "~2.2.0", 22 | "gulp": "~3.8.7", 23 | "gulp-clean": "~0.3.1", 24 | "gulp-concat": "~2.3.4", 25 | "gulp-if": "^1.2.5", 26 | "gulp-karma": "0.0.4", 27 | "gulp-less": "^1.3.6", 28 | "gulp-minify-css": "~0.3.7", 29 | "gulp-minify-html": "~0.1.4", 30 | "gulp-ng-annotate": "^0.5.2", 31 | "gulp-rimraf": "^0.1.1", 32 | "gulp-sourcemaps": "~1.1.1", 33 | "gulp-svgmin": "~0.4.6", 34 | "gulp-uglify": "~0.3.1", 35 | "karma": "0.12.21", 36 | "karma-growl-reporter": "0.1.1", 37 | "karma-jasmine": "0.1.5", 38 | "karma-phantomjs-launcher": "0.1.4", 39 | "lodash": "~2.4.1", 40 | "main-bower-files": "~1.0.2", 41 | "merge-stream": "~0.1.5", 42 | "through2": "~0.5.1", 43 | "vinyl": "~0.3.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /projectFilesBackup/.idea/runConfigurations/Karma.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /projectFilesBackup/.idea/runConfigurations/gulpfile_js.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /projectFilesBackup/.idea/runConfigurations/gulpfile_js_dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /projectFilesBackup/.idea/runConfigurations/gulpfile_js_sandbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/angular-form-gen/angular-form-gen.css: -------------------------------------------------------------------------------- 1 | .fg-field-required .control-label:after { 2 | content: ' *'; 3 | color: #888; 4 | } 5 | 6 | .fg-legend-extra { 7 | color: #999; 8 | font-weight: lighter; 9 | display: inline-block; 10 | } 11 | -------------------------------------------------------------------------------- /src/angular-form-gen/angular-form-gen.test.js: -------------------------------------------------------------------------------- 1 | describe('fgConfigProvider', function () { 2 | 3 | var configProvider; 4 | 5 | beforeEach(function () { 6 | // Initialize the service provider 7 | // by injecting it to a fake module's config block 8 | var fakeModule = angular.module('this.is.so.fake', function () {}); 9 | fakeModule.config(function (fgConfigProvider) { 10 | configProvider = fgConfigProvider; 11 | }); 12 | // Initialize test.app injector 13 | module('fg', 'this.is.so.fake'); 14 | 15 | // Kickstart the injectors previously registered 16 | // with calls to angular.mock.module 17 | inject(function () {}); 18 | }); 19 | 20 | it('it should resolve the fgConfigProvider', function () { 21 | expect(configProvider).not.toBeUndefined(); 22 | }); 23 | 24 | it('should get field template by type', function () { 25 | 26 | var result = configProvider.fields.get('text'); 27 | 28 | expect(result).toBeDefined(); 29 | expect(result.type).toBe('text'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/angular-form-gen/common/bind-expression.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgBindExpression', function ($interpolate) { 2 | 3 | function buildWatchExpression(interpolateFn) { 4 | var sb = []; 5 | var parts = interpolateFn.parts; 6 | var ii = parts.length; 7 | 8 | while (ii--) { 9 | var part = parts[ii]; 10 | 11 | if (part.exp && !part.exp.match(/^\s*$/)) { 12 | sb.push(part.exp); 13 | } 14 | } 15 | 16 | return '[' + sb.join() + ']'; 17 | } 18 | 19 | return function (scope, element, attr) { 20 | 21 | var interpolateFn, watchHandle, oldWatchExpr; 22 | 23 | function cleanWatchHandle() { 24 | if (watchHandle) watchHandle(); 25 | watchHandle = undefined; 26 | } 27 | 28 | function interpolateExpression() { 29 | element.text(interpolateFn(scope)); 30 | } 31 | 32 | scope.$on('$destroy', function () { 33 | cleanWatchHandle(); 34 | }); 35 | 36 | scope.$watch(attr.fgBindExpression, function (value) { 37 | if (value !== undefined) { 38 | interpolateFn = $interpolate(value); 39 | 40 | element.addClass('ng-binding').data('$binding', interpolateFn); 41 | 42 | var watchExpr = buildWatchExpression(interpolateFn); 43 | 44 | if (oldWatchExpr !== watchExpr) { 45 | 46 | oldWatchExpr = watchExpr; 47 | 48 | cleanWatchHandle(); 49 | 50 | watchHandle = scope.$watchCollection(watchExpr, function () { 51 | interpolateExpression(); 52 | }); 53 | } else { 54 | interpolateExpression(); 55 | } 56 | } 57 | }); 58 | }; 59 | }); 60 | -------------------------------------------------------------------------------- /src/angular-form-gen/common/fg-dropdown-input.css: -------------------------------------------------------------------------------- 1 | .fg-dropdown { 2 | z-index: 50; 3 | } 4 | 5 | .fg-dropdown.open { 6 | position: absolute; 7 | } 8 | 9 | .fg-dropdown .dropdown-menu { 10 | position: static; 11 | top: 0; left: 0; 12 | float: none; 13 | max-height: 250px; 14 | overflow-y: auto; 15 | } -------------------------------------------------------------------------------- /src/angular-form-gen/common/fg-null-form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by null on 16/10/14. 3 | */ 4 | fg.directive('fgNullForm', function () { 5 | 6 | var nullFormCtrl = { 7 | $addControl: angular.noop, 8 | $removeControl: angular.noop, 9 | $setValidity: angular.noop, 10 | $setDirty: angular.noop, 11 | $setPristine: angular.noop 12 | }; 13 | 14 | return { 15 | restrict: 'A', 16 | require: ['form'], 17 | link: function link($scope, $element, $attrs, $ctrls) { 18 | 19 | var form = $ctrls[0]; 20 | 21 | // Locate the parent form 22 | 23 | var parentForm = $element.parent().inheritedData('$formController'); 24 | 25 | if(parentForm) { 26 | 27 | // Unregister this form controller 28 | 29 | parentForm.$removeControl(form); 30 | } 31 | 32 | // Nullify the form 33 | 34 | angular.extend(form, nullFormCtrl); 35 | } 36 | }; 37 | }); 38 | 39 | fg.directive('fgFormRequiredFilter', function() { 40 | 41 | return { 42 | restrict: 'A', 43 | require: ['form'], 44 | link: function($scope, $element, $attrs, $ctrls) { 45 | 46 | var form = $ctrls[0]; 47 | 48 | var $setValidity = form.$setValidity; 49 | 50 | form.$setValidity = function (validationToken, isValid, control) { 51 | 52 | if(validationToken === 'required') { 53 | isValid = true; 54 | } 55 | 56 | $setValidity.call(form, validationToken, isValid, control); 57 | }; 58 | } 59 | }; 60 | 61 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/common/fg-placeholder-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgPlaceholder', function() { 2 | /* 3 | This attribute is only required on TEXTAREA elements. 4 | Angular in combination with IE doesn't like placeholder="{{ myExpression }}". 5 | */ 6 | return { 7 | link: function($scope, $element, $attrs) { 8 | $scope.$watch($attrs.fgPlaceholder, function(value) { 9 | $element.attr('placeholder', value); 10 | }); 11 | } 12 | }; 13 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/common/jsonify/jsonify.css: -------------------------------------------------------------------------------- 1 | /*.jsonify {*/ 2 | /*position: relative;*/ 3 | /*margin: 15px 0;*/ 4 | /*padding: 39px 19px 14px;*/ 5 | /*background-color: #fff;*/ 6 | /*border: 1px solid #ddd;*/ 7 | /*-webkit-border-radius: 4px;*/ 8 | /*-moz-border-radius: 4px;*/ 9 | /*border-radius: 4px;*/ 10 | /*}*/ 11 | 12 | /*.jsonify-label {*/ 13 | /*position: absolute;*/ 14 | /*top: -1px;*/ 15 | /*left: -1px;*/ 16 | /*padding: 3px 7px;*/ 17 | /*font-size: 12px;*/ 18 | /*font-weight: bold;*/ 19 | /*background-color: #f5f5f5;*/ 20 | /*border: 1px solid #ddd;*/ 21 | /*color: #9da0a4;*/ 22 | /*-webkit-border-radius: 4px 0 4px 0;*/ 23 | /*-moz-border-radius: 4px 0 4px 0;*/ 24 | /*border-radius: 4px 0 4px 0;*/ 25 | /*}*/ 26 | 27 | /*.jsonify-button {*/ 28 | /*display: none;*/ 29 | /*cursor: pointer;*/ 30 | /*}*/ 31 | 32 | /*.jsonify:hover .jsonify-button {*/ 33 | /*display: inline-block;*/ 34 | /*}*/ 35 | 36 | .jsonify { 37 | margin-top: 10px; 38 | position: relative; 39 | } 40 | 41 | .jsonify .btn-toolbar-right { 42 | right: 5px; 43 | } 44 | .jsonify .btn-toolbar { 45 | position: absolute; 46 | margin: 0px; 47 | top: -10px; 48 | } -------------------------------------------------------------------------------- /src/angular-form-gen/common/jsonify/jsonify.js: -------------------------------------------------------------------------------- 1 | fg.filter('j$on',function () { 2 | return function (input, displayHidden) { 3 | 4 | if(displayHidden) 5 | return JSON.stringify(input || {}, null, ' '); 6 | 7 | return angular.toJson(input || {}, true); 8 | }; 9 | }).directive('jsonify', function ($window, $filter) { 10 | return { 11 | templateUrl: 'angular-form-gen/common/jsonify/jsonify.ng.html', 12 | replace: true, 13 | scope: { 14 | jsonify: "=", 15 | displayHidden: "@jsonifyDisplayHidden" 16 | }, 17 | link: function($scope, $element, $attrs, ctrls) { 18 | $scope.expression = $attrs.jsonify; 19 | 20 | $scope.copy = function() { 21 | $window.prompt ("Copy to clipboard: Ctrl+C, Enter", $filter('j$on')($scope.jsonify, $scope.displayHidden)); 22 | }; 23 | } 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /src/angular-form-gen/common/jsonify/jsonify.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 8 | 14 |
15 |
{{ jsonify | j$on:displayHidden }}
16 |
-------------------------------------------------------------------------------- /src/angular-form-gen/common/tabs/tabs-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgTabsController', function ($scope) { 2 | 3 | this.items = []; 4 | this.active = null; 5 | this.activeIndex = -1; 6 | 7 | this.add = function (item) { 8 | this.items.push(item); 9 | 10 | this.items.sort(function (x, y) { 11 | return x.order - y.order; 12 | }); 13 | 14 | if (!$scope.active && item.autoActive != false) { 15 | this.activate(item); 16 | } 17 | }; 18 | 19 | this.activate = function (itemOrIndex) { 20 | 21 | var idx = -1, item; 22 | 23 | if (isNaN(itemOrIndex)) { 24 | 25 | // Locate the item index 26 | 27 | item = itemOrIndex; 28 | var i = this.items.length; 29 | 30 | while (i--) { 31 | if (this.items[i] === item) { 32 | idx = i; 33 | break; 34 | } 35 | } 36 | 37 | if (idx === -1) { 38 | throw new Error('Cannot activate pane: not found in pane list.'); 39 | } 40 | } else { 41 | 42 | // Grab the item at the provided index 43 | 44 | idx = itemOrIndex; 45 | 46 | if(idx < 0 || idx >= this.items.length) { 47 | throw new Error('Cannot activate pane: index out of bounds.') 48 | } 49 | 50 | item = this.items[idx]; 51 | } 52 | 53 | if (!item.disabled) { 54 | this.active = $scope.active = item; 55 | this.activeIndex = $scope.activeIndex = idx; 56 | } 57 | 58 | }; 59 | 60 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/common/tabs/tabs-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgTabs', function() { 2 | return { 3 | require: ['fgTabs'], 4 | restrict: 'EA', 5 | transclude: true, 6 | controller: 'fgTabsController', 7 | templateUrl: 'angular-form-gen/common/tabs/tabs.ng.html', 8 | scope: { 9 | 'tabs': '=?fgTabs', 10 | 'active': '=?fgTabsActive', 11 | 'activeIndex': '=?fgTabsActiveIndex' 12 | }, 13 | link: function($scope, $element, $attrs, $ctrls) { 14 | $scope.tabs = $ctrls[0]; 15 | 16 | $scope.$watch('activeIndex', function(value) { 17 | if(value !== undefined && $scope.tabs.activeIndex !== value) { 18 | $scope.tabs.activate(value); 19 | } 20 | }); 21 | } 22 | }; 23 | }); 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/angular-form-gen/common/tabs/tabs-pane-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgTabsPane', function(fgTabsPaneLinkFn) { 2 | return { 3 | require: ['^fgTabs'], 4 | restrict: 'EA', 5 | transclude: true, 6 | templateUrl: 'angular-form-gen/common/tabs/tabs-pane.ng.html', 7 | link: fgTabsPaneLinkFn, 8 | scope: true 9 | }; 10 | }).factory('fgTabsPaneLinkFn', function() { 11 | return function($scope, $element, $attrs, $ctrls) { 12 | 13 | $scope.tabs = $ctrls[0]; 14 | 15 | $scope.pane = { 16 | title: $attrs.fgTabsPane || $attrs.title, 17 | order: parseInt($attrs.fgTabsPaneOrder || $attrs.order) || 10, 18 | autoActive: !($attrs.fgTabsPaneAutoActive === "false" || $attrs.autoActive === "false"), 19 | renderAlways: $attrs.fgTabsPaneRenderAlways === "true" || $attrs.renderAlways === "true" 20 | }; 21 | 22 | $scope.$watch($attrs.fgTabsPaneDisabled, function(value) { 23 | $scope.pane.disabled = value; 24 | }); 25 | 26 | $scope.tabs.add($scope.pane); 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /src/angular-form-gen/common/tabs/tabs-pane.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
-------------------------------------------------------------------------------- /src/angular-form-gen/common/tabs/tabs.css: -------------------------------------------------------------------------------- 1 | .fg-tabs > .nav-tabs { 2 | margin-bottom: 0px; 3 | } 4 | 5 | .fg-tabs .tab-content { 6 | padding: 10px; 7 | border: 1px solid #ddd; 8 | border-top: none; 9 | border-radius: 0px 0px 4px 4px; 10 | } -------------------------------------------------------------------------------- /src/angular-form-gen/common/tabs/tabs.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 |
-------------------------------------------------------------------------------- /src/angular-form-gen/drag/angular-drag-queen.js: -------------------------------------------------------------------------------- 1 | angular.module('dq', []).factory('dqUtils', function($window, $rootScope) { 2 | 3 | var _dragData = null; 4 | 5 | //noinspection FunctionWithInconsistentReturnsJS 6 | return { 7 | getEvent: function (e) { 8 | return e && e.originalEvent ? e.originalEvent : e || $window.event; 9 | }, 10 | stopEvent: function (e) { 11 | // e.cancelBubble is supported by IE8 - 12 | // this will kill the bubbling process. 13 | e.cancelBubble = true; 14 | e.bubbles = false; 15 | 16 | // e.stopPropagation works in modern browsers 17 | if (e.stopPropagation) e.stopPropagation(); 18 | if (e.preventDefault) e.preventDefault(); 19 | 20 | return false; 21 | }, 22 | dragData: function (data) { 23 | if (data === undefined) { 24 | return _dragData; 25 | } 26 | _dragData = data; 27 | }, 28 | getParentArea: function ($scope) { 29 | var area = {}; 30 | $scope.$emit('dqLocateArea', area); 31 | return area.name; 32 | }, 33 | isAreaMatch: function ($scope) { 34 | var parentArea = this.getParentArea($scope); 35 | var eventArea = _dragData ? _dragData.area : ""; 36 | 37 | return parentArea === eventArea; 38 | } 39 | }; 40 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/drag/dq-drag-area.js: -------------------------------------------------------------------------------- 1 | angular.module('dq').directive('dqDragArea', function (dqUtils) { 2 | 3 | function evalBroadcastEvent($scope, args, areaName, expression) { 4 | if (expression && args && args.area === areaName) { 5 | $scope.$eval(expression); 6 | } 7 | } 8 | 9 | return { 10 | restrict: 'AEC', 11 | link: function ($scope, $element, $attrs) { 12 | 13 | var areaName = $attrs.dqDragArea || $attrs.dqDragAreaName || ""; 14 | 15 | $scope.$on('dqDragBegin', function ($event, args) { 16 | evalBroadcastEvent($scope, args, areaName, $attrs.dqDragProgressBegin); 17 | }); 18 | 19 | $scope.$on('dqDragEnd', function ($event, args) { 20 | evalBroadcastEvent($scope, args, areaName, $attrs.dqDragProgressEnd); 21 | }); 22 | 23 | $scope.$on('dqLocateArea', function($event, args) { 24 | args.name = areaName; 25 | $event.stopPropagation(); 26 | }); 27 | } 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /src/angular-form-gen/drag/dq-draggable.js: -------------------------------------------------------------------------------- 1 | angular.module('dq').directive('dqDraggable', function (dqUtils, $rootScope) { 2 | 3 | function evalAndBroadcast(eventName, targetArea, $scope, expression, cb) { 4 | $scope.$apply(function () { 5 | var data = $scope.$eval(expression); 6 | 7 | var bcData = { 8 | area: targetArea, 9 | data: data 10 | }; 11 | 12 | cb(bcData); 13 | 14 | $rootScope.$broadcast(eventName, bcData); 15 | }); 16 | } 17 | 18 | return { 19 | restrict: 'AEC', 20 | link: function ($scope, $element, $attrs) { 21 | 22 | var targetArea = $attrs.dqDraggable || $attrs.dqDragTargetArea || ""; 23 | var disabled = false; 24 | 25 | $scope.$watch($attrs.dqDragDisabled, function(value) { 26 | disabled = value; 27 | $element.attr('draggable', disabled ? 'false' : 'true'); 28 | }); 29 | 30 | $element.on('selectstart',function (e) { 31 | 32 | // Pure IE evilness 33 | 34 | if (!disabled && this.dragDrop) { 35 | this.dragDrop(); 36 | e = dqUtils.getEvent(e); 37 | return dqUtils.stopEvent(e); 38 | } 39 | }).on('dragstart',function (e) { 40 | 41 | e = dqUtils.getEvent(e); 42 | 43 | if(disabled) { 44 | return dqUtils.stopEvent(e); 45 | } 46 | 47 | var dt = e.dataTransfer; 48 | dt.effectAllowed = 'all'; 49 | dt.setData('Text', 'The cake is a lie!'); 50 | 51 | evalAndBroadcast('dqDragBegin', targetArea, $scope, $attrs.dqDragBegin, function(dragData) { 52 | dqUtils.dragData(dragData); 53 | }); 54 | 55 | }).on('dragend', function () { 56 | 57 | evalAndBroadcast('dqDragEnd', targetArea, $scope, $attrs.dqDragEnd, function() { 58 | dqUtils.dragData(null); 59 | }); 60 | 61 | }); 62 | } 63 | }; 64 | 65 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/canvas-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgEditCanvasController', function ($scope, dqUtils, $timeout, fgUtils) { 2 | 3 | $scope.dragPlaceholder = { 4 | visible: false, 5 | index: 0 6 | }; 7 | 8 | // - - - 8-< - - - - - - - - - - - - - - - - - - - - - 9 | // Drag & drop 10 | // - - - 8-< - - - - - - - - - - - - - - - - - - - - - 11 | 12 | $scope.$on('dqDragBegin', function() { 13 | $scope.dragging = true; 14 | }); 15 | 16 | $scope.$on('dqDragEnd', function() { 17 | $scope.dragging = false; 18 | }); 19 | 20 | this.dragEnter = function () { 21 | // $scope.dragging = true; 22 | $scope.dragPlaceholder.visible = true; 23 | $scope.dragPlaceholder.index = $scope.schema.fields.length; 24 | }; 25 | 26 | this.dragLeave = function () { 27 | $scope.dragPlaceholder.visible = false; 28 | }; 29 | 30 | this.dragBeginCanvasField = function (index, field) { 31 | 32 | // Delay is set to prevent browser from copying adjusted html as copy image 33 | 34 | $timeout(function () { 35 | field.$_isDragging = true; 36 | }, 1); 37 | 38 | return { source: 'canvas', field: field, index: index }; 39 | }; 40 | 41 | this.dragEndCanvasField = function (field) { 42 | 43 | // IE Fix: ensure this is fired after the drag begin 44 | 45 | $timeout(function () { 46 | field.$_isDragging = false; 47 | // $scope.dragging = false; 48 | }, 10); 49 | 50 | }; 51 | 52 | this.drop = function () { 53 | 54 | var dragData = dqUtils.dragData(); 55 | 56 | if (dragData && dragData.data) { 57 | 58 | var field = dragData.data.field; 59 | var source = dragData.data.source; 60 | var index = dragData.data.index; 61 | var fields = $scope.schema.fields; 62 | 63 | if (source == 'palette') { 64 | $scope.schemaCtrl.addField(field, $scope.dragPlaceholder.index); 65 | 66 | } else if (source == 'canvas') { 67 | $scope.schemaCtrl.moveField(index, $scope.dragPlaceholder.index); 68 | 69 | // fields.splice(index, 1); 70 | // fields.splice($scope.dragPlaceholder.index, 0, field); 71 | } 72 | 73 | // IE fix: not calling dragEnd sometimes 74 | field.$_isDragging = false; 75 | } else { 76 | throw Error('Drop without data'); 77 | } 78 | }; 79 | 80 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/canvas-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEditCanvas', function() { 2 | 3 | return { 4 | require: ['^fgEdit', '^fgSchema', '^form'], 5 | templateUrl: 'angular-form-gen/edit/canvas/canvas.ng.html', 6 | controller: 'fgEditCanvasController as canvasCtrl', 7 | link: function($scope, $element, $attrs, ctrls) { 8 | $scope.editCtrl = ctrls[0]; 9 | $scope.schemaCtrl = ctrls[1]; 10 | $scope.formCtrl = ctrls[2]; 11 | 12 | var ignoreDirty = true; 13 | 14 | $scope.$watchCollection('schema.fields', function() { 15 | 16 | // Ignore the first call, $watchCollection fires at once without any changes. 17 | 18 | if(!ignoreDirty) { 19 | $scope.formCtrl.$setDirty(true); 20 | } 21 | 22 | ignoreDirty = false; 23 | 24 | }); 25 | } 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/canvas-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-edit-canvas-directive', function() { 2 | 3 | var editCtrlMock = {}, schema = {}, schemaCtrlMock = { 4 | model: function() { return schema; } 5 | }, $compile, $scope; 6 | 7 | beforeEach(function() { 8 | 9 | module('fg'); 10 | 11 | inject(function (_$compile_, _$rootScope_) { 12 | 13 | $compile = _$compile_; 14 | 15 | $scope = _$rootScope_.$new(); 16 | 17 | // This is not an isolated scope -- the directive requires 18 | // the scope controller to be set, which sets the schema property 19 | // on the scope. 20 | 21 | $scope.schema = schema; 22 | 23 | // Needed for the require directive flag 24 | 25 | $element = angular.element('
'); 26 | $element.data('$fgEditController', editCtrlMock); 27 | $element.data('$fgSchemaController', schemaCtrlMock); 28 | 29 | }); 30 | 31 | }); 32 | 33 | it('should compile directive', function() { 34 | 35 | // Arrange 36 | 37 | var template = '
'; 38 | 39 | $element.append(template); 40 | 41 | // Act 42 | 43 | $compile($element)($scope); 44 | $scope.$digest(); 45 | var result = $element.find('.fg-edit-canvas'); 46 | 47 | // Assert 48 | 49 | expect(result.length).toBe(1); 50 | }); 51 | 52 | it('should persist edit controller to scope', function() { 53 | 54 | // Arrange 55 | 56 | var template = '
'; 57 | 58 | $element.append(template); 59 | $compile($element)($scope); 60 | $scope.$digest(); 61 | 62 | // Act 63 | 64 | var $canvasScope = $element.find('.fg-edit-canvas').scope(); 65 | 66 | // Assert 67 | 68 | expect($canvasScope).toBeDefined(); 69 | expect($canvasScope.editCtrl).toBeDefined(); 70 | expect($canvasScope.editCtrl).toBe(editCtrlMock); 71 | 72 | }); 73 | 74 | it('should have schema on scope', function() { 75 | 76 | // Arrange 77 | 78 | var template = '
'; 79 | 80 | $element.append(template); 81 | $compile($element)($scope); 82 | $scope.$digest(); 83 | 84 | // Act 85 | 86 | var $canvasScope = $element.find('.fg-edit-canvas').scope(); 87 | 88 | // Assert 89 | 90 | expect($canvasScope).toBeDefined(); 91 | expect($canvasScope.schema).toBeDefined(); 92 | }); 93 | 94 | }); 95 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/canvas.css: -------------------------------------------------------------------------------- 1 | 2 | .fg-edit-canvas .fg-field-properties .control-group { 3 | margin-bottom: 10px; 4 | } 5 | 6 | .fg-edit-canvas .fg-field.fg-edit-canvas-field { 7 | padding-top: 15px; 8 | padding-bottom: 15px; 9 | } 10 | 11 | /*.fg-edit-canvas .control-label {*/ 12 | /*width: 130px;*/ 13 | /*}*/ 14 | 15 | /*.fg-edit-canvas .controls {*/ 16 | /*margin-left: 150px;*/ 17 | /*}*/ 18 | 19 | .fg-edit-canvas-area { 20 | min-height: 340px; 21 | /*position: relative;*/ 22 | -webkit-border-radius: 4px; 23 | -moz-border-radius: 4px; 24 | border-radius: 4px; 25 | border: 1px solid #ddd; 26 | padding: 20px; 27 | } 28 | 29 | .fg-edit-canvas-area-empty { 30 | /*position: absolute;*/ 31 | left: 20px; 32 | right: 20px; 33 | bottom: 20px; 34 | top: 20px; 35 | margin: 0; 36 | /*padding: 0;*/ 37 | } 38 | 39 | .fg-edit-canvas-area-empty-x { 40 | font-size: 200px; 41 | line-height: 200px; 42 | } 43 | 44 | 45 | .fg-drag-placeholder { 46 | margin-bottom: 0; 47 | opacity: 0.0; 48 | filter: aplha(opacity(0)); 49 | transition: height 0ms linear, opacity 0ms; 50 | height: 0px; 51 | display: none; 52 | } 53 | 54 | /*.fg-edit-canvas-dragging {*/ 55 | /*background-color: teal;*/ 56 | /*}*/ 57 | 58 | .fg-edit-canvas-dragging .fg-drag-placeholder { 59 | display: block; 60 | transition: height 100ms linear, opacity 500ms, margin-bottom 100ms; 61 | } 62 | 63 | .fg-drag-placeholder-visible { 64 | 65 | margin-bottom: 20px; 66 | border-radius: 4px; 67 | border: dashed 1px #3a87ad; 68 | background-color: #d9edf7; 69 | height: 74px; 70 | opacity: 1.0; 71 | filter: aplha(opacity(100)); 72 | } 73 | 74 | .fg-field-overlay-drag-top, .fg-field-overlay-drag-bottom { 75 | height: 50%; 76 | } 77 | 78 | .fg-field-properties .fg-field-not-in-cache { 79 | margin-left: 20px; 80 | margin-right: 20px; 81 | margin-bottom: 0px; 82 | } 83 | 84 | .fg-edit-canvas .fg-field.dragging { 85 | display: none; 86 | } 87 | 88 | /* Landscape phones and down */ 89 | /*@media (max-width: 480px) {*/ 90 | 91 | /**/ 92 | /*.fg-edit-canvas .controls {*/ 93 | /*margin-left: 0px;*/ 94 | /*}*/ 95 | /*}*/ 96 | 97 | @media (max-width: 768px) { 98 | .fg-edit-canvas-area { 99 | /*padding-bottom: 0;*/ 100 | min-height: inherit; 101 | padding: 0px; 102 | border: none; 103 | 104 | } 105 | 106 | .fg-edit-canvas-area-empty { 107 | position: static; 108 | margin-bottom: 20px; 109 | } 110 | } -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/canvas.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | Canvas 5 |
10 |
11 |
12 |

X

13 |

Drag one of the available templates from the 14 | palette onto this canvas.

15 |
16 |
17 |
18 | 19 |
21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/field-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEditCanvasField', function ($timeout) { 2 | 3 | return { 4 | templateUrl: 'angular-form-gen/edit/canvas/field/field.ng.html', 5 | link: function ($scope) { 6 | 7 | // Prevent the property tabs from closing if the field schema is invalid 8 | 9 | $scope.toggleProperties = function (field) { 10 | if (field.$_displayProperties) { 11 | field.$_displayProperties = field.$_invalid; 12 | } else { 13 | field.$_displayProperties = true; 14 | } 15 | } 16 | 17 | $scope.$watch('field.$_displayProperties', function (value) { 18 | 19 | if (value) { 20 | $scope.expanded = true; 21 | } else { 22 | $timeout(function () { 23 | $scope.expanded = false; 24 | }, 550); 25 | 26 | } 27 | 28 | 29 | }); 30 | } 31 | }; 32 | 33 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/field.css: -------------------------------------------------------------------------------- 1 | .fg-field-properties-container { 2 | max-height: 0; 3 | overflow: hidden; 4 | transition: .5s; 5 | opacity: 0; 6 | } 7 | 8 | .fg-field-properties-container.visible { 9 | max-height: 1000px; 10 | opacity: 1; 11 | } 12 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/field.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 9 | 12 | 15 | 18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 |
28 | 29 |
-------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/options/options-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgPropertyFieldOptions', function(fgPropertyFieldOptionsLinkFn) { 2 | return { 3 | scope: true, 4 | controller: 'fgPropertyFieldOptionsController as optionsCtrl', 5 | templateUrl: 'angular-form-gen/edit/canvas/field/properties/options/options.ng.html', 6 | link: fgPropertyFieldOptionsLinkFn 7 | }; 8 | }).factory('fgPropertyFieldOptionsLinkFn', function() { 9 | return function($scope, $element, $attrs, ctrls) { 10 | 11 | $scope.multiple = false; 12 | 13 | $attrs.$observe('fgPropertyFieldOptions', function(value) { 14 | if(value === 'multiple') { 15 | $scope.multiple = true; 16 | } 17 | }); 18 | }; 19 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/options/options-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('options-directive', function() { 2 | 3 | var $scope; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function($rootScope) { 10 | $scope = $rootScope.$new(); 11 | }); 12 | 13 | }); 14 | 15 | describe('when linking', function() { 16 | 17 | var linkFn; 18 | 19 | beforeEach(function() { 20 | 21 | inject(function(fgPropertyFieldOptionsLinkFn) { 22 | linkFn = fgPropertyFieldOptionsLinkFn; 23 | }); 24 | 25 | }); 26 | 27 | xit('should observe main attribute for field object', function() { 28 | 29 | // Arrange 30 | 31 | $scope.myField = {}; 32 | 33 | var $attrs = { 34 | fgPropertyFieldOptionsLinkFn: 'myField' 35 | }; 36 | 37 | // Act 38 | 39 | linkFn($scope, null, $attrs, []); 40 | $scope.$digest(); 41 | 42 | // Assert 43 | 44 | expect($scope.field).toBe($scope.myField); 45 | 46 | }); 47 | 48 | }); 49 | 50 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/options/options.css: -------------------------------------------------------------------------------- 1 | 2 | .table-field-options .form-control { 3 | width: auto; 4 | } -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/options/options.ng.html: -------------------------------------------------------------------------------- 1 |
2 |

No options defined

3 | 4 |

Click here to add a new option definition to this field.

5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 29 | 32 | 35 | 39 | 40 | 41 | 42 |
ValueText 14 | 16 |
23 | 24 | 26 | 28 | 30 | 31 | 33 | 34 | 36 | 38 |
43 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/properties-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEditCanvasFieldProperties', function (fgUtils) { 2 | 3 | // To keep the form validation working, the contents of the tabs needs to be rendered even if the tab is not active. 4 | 5 | function setRenderAlways(tabItems) { 6 | var i = tabItems.length; 7 | 8 | while (i--) { 9 | var tab = tabItems[i]; 10 | 11 | // Skip the debug tab 12 | 13 | if(tab.title !== 'Debug') { 14 | tab.renderAlways = true; 15 | } 16 | } 17 | } 18 | 19 | return { 20 | templateUrl: 'angular-form-gen/edit/canvas/field/properties/properties.ng.html', 21 | scope: { 22 | field: '=fgEditCanvasFieldProperties' 23 | }, 24 | link: { 25 | pre: function ($scope) { 26 | $scope.property = {}; 27 | }, 28 | post: function ($scope) { 29 | 30 | $scope.$watch('fieldPropertiesForm.$invalid', function (newValue) { 31 | $scope.field.$_invalid = newValue; 32 | }); 33 | 34 | $scope.renderInfo = fgUtils.getRenderInfo($scope.field); 35 | 36 | 37 | $scope.$watch('property.tabs.items.length', function(value) { 38 | if(value) { 39 | setRenderAlways($scope.property.tabs.items); 40 | } 41 | }); 42 | 43 | } 44 | } 45 | }; 46 | }); 47 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/properties.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
-------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/common-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgPropertyFieldCommon', function(fgPropertyFieldCommonLinkFn) { 2 | return { 3 | restrict: 'AE', 4 | templateUrl: 'angular-form-gen/edit/canvas/field/properties/property-field/common.ng.html', 5 | link: fgPropertyFieldCommonLinkFn 6 | }; 7 | }).factory('fgPropertyFieldCommonLinkFn', function() { 8 | return function($scope, $element, $attrs, ctrls) { 9 | 10 | $scope.fields = { 11 | fieldname: false, 12 | displayname: false, 13 | placeholder: false, 14 | tooltip: false, 15 | focus: false 16 | }; 17 | 18 | $scope.$watch($attrs['fgPropertyFieldCommon'], function(value) { 19 | $scope.fields = angular.extend($scope.fields, value); 20 | }); 21 | }; 22 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/common-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-property-field-common', function() { 2 | 3 | var $scope; 4 | 5 | beforeEach(function() { 6 | module('fg'); 7 | 8 | inject(function($rootScope) { 9 | $scope = $rootScope.$new(); 10 | }); 11 | }); 12 | 13 | describe('when linking', function() { 14 | 15 | var fgPropertyFieldCommonLinkFn; 16 | 17 | beforeEach(function() { 18 | 19 | inject(function(_fgPropertyFieldCommonLinkFn_) { 20 | fgPropertyFieldCommonLinkFn = _fgPropertyFieldCommonLinkFn_; 21 | }); 22 | }); 23 | 24 | it('should observe and extend the fgPropertyFieldCommon attribute into the fields scope value', function() { 25 | 26 | // Arrange 27 | 28 | var $attrs = { 29 | fgPropertyFieldCommon: '{ fieldname: true, myOtherProp: "gedoe" }' 30 | }; 31 | 32 | // Act 33 | 34 | fgPropertyFieldCommonLinkFn($scope, {}, $attrs, []); 35 | $scope.$digest(); 36 | 37 | // Assert 38 | 39 | expect($scope.fields).toBeDefined(); 40 | expect($scope.fields.fieldname).toBe(true); 41 | expect($scope.fields.displayname).toBeFalsy(); 42 | expect($scope.fields.placeholder).toBeFalsy(); 43 | expect($scope.fields.myOtherProp).toBe('gedoe'); 44 | }); 45 | }); 46 | 47 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/common.ng.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 23 |
24 |
25 | 26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 | 34 |
35 |
36 | 40 |
41 |
42 | 43 | 44 |
45 |
46 | 50 |
51 |
52 | 53 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/field-value.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
-------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/property-field-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgPropertyField', function(fgPropertyFieldLinkFn) { 2 | 3 | return { 4 | restrict: 'AE', 5 | templateUrl: 'angular-form-gen/edit/canvas/field/properties/property-field/property-field.ng.html', 6 | transclude: true, 7 | scope: true, 8 | link: fgPropertyFieldLinkFn 9 | }; 10 | 11 | }).factory('fgPropertyFieldLinkFn', function() { 12 | return function($scope, $element, $attrs, ctrls) { 13 | 14 | $attrs.$observe('fgPropertyField', function(value) { 15 | $scope.fieldName = value; 16 | }); 17 | 18 | $attrs.$observe('fgPropertyFieldLabel', function(value) { 19 | if(value) { 20 | $scope.fieldLabel = value; 21 | } 22 | }); 23 | 24 | }; 25 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/property-field-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-property-field-directive', function() { 2 | 3 | var $scope; 4 | 5 | beforeEach(function() { 6 | module('fg'); 7 | 8 | inject(function($rootScope) { 9 | $scope = $rootScope.$new(); 10 | }); 11 | }); 12 | 13 | describe('when linking', function() { 14 | 15 | var fgPropertyFieldLinkFn; 16 | 17 | beforeEach(function() { 18 | 19 | inject(function(_fgPropertyFieldLinkFn_) { 20 | fgPropertyFieldLinkFn = _fgPropertyFieldLinkFn_; 21 | }); 22 | }); 23 | 24 | it('should observe the fgPropertyField attribute', function() { 25 | 26 | // Arrange 27 | 28 | var myFieldName = "12345"; 29 | 30 | var $attrs = { 31 | $observe: function(attribute, fn) { 32 | if(attribute === 'fgPropertyField') { 33 | fn(myFieldName); 34 | } 35 | } 36 | }; 37 | 38 | // Act 39 | 40 | fgPropertyFieldLinkFn($scope, {}, $attrs, []); 41 | $scope.$digest(); 42 | 43 | // Assert 44 | 45 | expect($scope.fieldName).toBe(myFieldName); 46 | 47 | }); 48 | 49 | it('should observe the fgPropertyFieldLabel attribute', function() { 50 | 51 | // Arrange 52 | 53 | var myFieldLabel = "12345"; 54 | 55 | var $attrs = { 56 | $observe: function(attribute, fn) { 57 | if(attribute === 'fgPropertyFieldLabel') { 58 | fn(myFieldLabel); 59 | } 60 | } 61 | }; 62 | 63 | // Act 64 | 65 | fgPropertyFieldLinkFn($scope, {}, $attrs, []); 66 | $scope.$digest(); 67 | 68 | // Assert 69 | 70 | expect($scope.fieldLabel).toBe(myFieldLabel); 71 | 72 | }); 73 | }); 74 | 75 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/property-field/property-field.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 |
7 |
8 |
9 |
10 |
-------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/validation/parse-pattern-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgParsePattern', function() { 2 | 3 | return { 4 | require: ['ngModel'], 5 | link: function($scope, $element, $attrs, ctrls) { 6 | var ngModelCtrl = ctrls[0]; 7 | 8 | ngModelCtrl.$parsers.push(validate); 9 | 10 | function validate(value) { 11 | try { 12 | new RegExp(value); 13 | } catch(e) { 14 | ngModelCtrl.$setValidity('pattern', false); 15 | return undefined; 16 | } 17 | 18 | ngModelCtrl.$setValidity('pattern', true); 19 | return value; 20 | } 21 | } 22 | }; 23 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/validation/validation-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgPropertyFieldValidation', function(fgPropertyFieldValidationLinkFn) { 2 | return { 3 | restrict: 'A', 4 | templateUrl: 'angular-form-gen/edit/canvas/field/properties/validation/validation.ng.html', 5 | link: fgPropertyFieldValidationLinkFn 6 | }; 7 | }).factory('fgPropertyFieldValidationLinkFn', function(fgConfig) { 8 | 9 | var patternOptions = []; 10 | var patternConfig = fgConfig.validation.patterns; 11 | 12 | angular.forEach(patternConfig, function(value, text) { 13 | patternOptions.push({ value: value, text: text }); 14 | }); 15 | 16 | return function($scope, $element, $attrs, ctrls) { 17 | 18 | $scope.patternOptions = patternOptions; 19 | 20 | $scope.field.validation = $scope.field.validation || {}; 21 | $scope.field.validation.messages = $scope.field.validation.messages || {}; 22 | 23 | $scope.fields = { 24 | required: false, 25 | minlength: false, 26 | maxlength: false, 27 | pattern: false 28 | }; 29 | 30 | $scope.$watch($attrs['fgPropertyFieldValidation'], function(value) { 31 | $scope.fields = angular.extend($scope.fields, value); 32 | }); 33 | }; 34 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/validation/validation-message-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEditValidationMessage', function(fgEditValidationMessageLinkFn) { 2 | return { 3 | templateUrl: 'angular-form-gen/edit/canvas/field/properties/validation/validation-message.ng.html', 4 | link: fgEditValidationMessageLinkFn, 5 | scope: true 6 | }; 7 | }).factory('fgEditValidationMessageLinkFn', function() { 8 | 9 | var DEFAULT_TOOLTIP = "Enter a error message here that will be shown if this validation fails. If this field is empty a default message will be used."; 10 | 11 | return function($scope, $element, $attrs, ctrls) { 12 | $attrs.$observe('fgEditValidationMessage', function(value) { 13 | $scope.validationType = value; 14 | }); 15 | 16 | $attrs.$observe('fgEditValidationTooltip', function(value) { 17 | value = value || DEFAULT_TOOLTIP; 18 | $scope.tooltip = value; 19 | }); 20 | }; 21 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/validation/validation-message.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
4 | 10 |
11 |
-------------------------------------------------------------------------------- /src/angular-form-gen/edit/canvas/field/properties/validation/validation.css: -------------------------------------------------------------------------------- 1 | /*.fg-property-field-validation + .fg-property-field-validation,*/ 2 | /*.fg-property-field-validation + div > .fg-property-field-validation {*/ 3 | /*border-top: 1px solid #cccccc;*/ 4 | /*padding-top: 10px;*/ 5 | /*}*/ 6 | 7 | .fg-property-field-validation { 8 | padding: 5px 0; 9 | } 10 | 11 | .fg-property-field-validation div.checkbox { 12 | padding-top: 0; 13 | } -------------------------------------------------------------------------------- /src/angular-form-gen/edit/edit-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgEditController', function ($scope, fgUtils, $location) { 2 | 3 | // var self = this; 4 | 5 | // $scope.preview = $location.search().preview; 6 | // 7 | // this.setMetaForm = function(metaForm) { 8 | // self.metaForm = metaForm; 9 | // }; 10 | 11 | // this.togglePreview = function() { 12 | // $scope.preview = !$scope.preview; 13 | // }; 14 | 15 | // $scope.$watch(function () { 16 | // 17 | // var schema = $scope.schemaCtrl.model(); 18 | // 19 | // // Seems that this watch is sometimes fired after the scope has been destroyed(?) 20 | // 21 | // if (schema) { 22 | //// schema.$_invalid = self.metaForm ? self.metaForm.$invalid : false; 23 | //// 24 | //// if (!schema.$_invalid) { 25 | // 26 | // var fields = schema.fields; 27 | // 28 | // if (fields) { 29 | // 30 | // var i = fields.length; 31 | // 32 | // while (--i >= 0 && !schema.$_invalid) { 33 | // schema.$_invalid = fields[i].$_invalid; 34 | // } 35 | // } 36 | // } 37 | // 38 | // }); 39 | 40 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/edit-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEdit', function () { 2 | return { 3 | priority: 100, 4 | require: 'fgSchema', 5 | restrict: 'AE', 6 | scope: { 7 | // // The schema model to edit 8 | schema: '=?fgSchema' 9 | // // Boolean indicating wether to show the default form action buttons 10 | // actionsEnabled: '=?fgActionsEnabled', 11 | // // Callback function when the user presses save -- any argument named 'schema' is set to the schema model. 12 | // onSave: '&fgOnSave', 13 | // // Callback function when the user presses cancel -- any argument named 'schema' is set to the schema model. 14 | // onCancel: '&fgOnCancel', 15 | // // Boolean indicating wether the edit is in preview mode or not 16 | // preview: '=?fgPreview' 17 | }, 18 | replace: true, 19 | controller: 'fgEditController as editCtrl', 20 | templateUrl: 'angular-form-gen/edit/edit.ng.html', 21 | link: function ($scope, $element, $attrs, schemaCtrl) { 22 | 23 | if ($scope.schema === undefined) { 24 | $scope.schema = {}; 25 | } 26 | 27 | // if ($scope.actionsEnabled === undefined) { 28 | // $scope.actionsEnabled = true; 29 | // } 30 | // 31 | // if ($scope.preview === undefined) { 32 | // $scope.preview = false; 33 | // } 34 | 35 | schemaCtrl.model($scope.schema); 36 | $scope.schemaCtrl = schemaCtrl; 37 | } 38 | } 39 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/edit.css: -------------------------------------------------------------------------------- 1 | .form .fg-edit { 2 | margin-left: -15px; 3 | margin-right: -15px; 4 | } 5 | 6 | .fg-edit-canvas .fg-field, .fg-edit-palette .fg-field { 7 | -webkit-touch-callout: none; 8 | -webkit-user-select: none; 9 | -khtml-user-select: none; 10 | -moz-user-select: none; 11 | -ms-user-select: none; 12 | user-select: none; 13 | 14 | padding: 10px; 15 | margin-bottom: 20px; 16 | background-color: #fff; 17 | border: 1px solid #ddd; 18 | -webkit-border-radius: 4px; 19 | -moz-border-radius: 4px; 20 | border-radius: 4px; 21 | position: relative; 22 | } 23 | 24 | .fg-edit-canvas .fg-field, .fg-edit-palette .fg-field:hover { 25 | background-color: #f5f5f5; 26 | } 27 | 28 | .fg-edit-canvas-field .form-group, .fg-edit-palette .form-group { 29 | margin-top: 5px; 30 | margin-bottom: 0px; 31 | } 32 | 33 | .fg-field { 34 | min-height: 25px; 35 | } 36 | 37 | .fg-edit-canvas .fg-field.error { 38 | background-color: #f2dede; 39 | border-color: #b94a48; 40 | } 41 | 42 | .fg-field-overlay { 43 | position: absolute; 44 | top: 0; 45 | right: 0; 46 | bottom: 0; 47 | left: 0; 48 | z-index:10; 49 | /*-moz-opacity: 0.0;*/ 50 | /*opacity:.0;*/ 51 | 52 | /* Needed by IE */ 53 | filter: alpha(opacity=0); 54 | background: #FFF; 55 | background-color: rgba(255, 255, 255, 0.0); 56 | cursor: move; 57 | } 58 | 59 | .fg-field-overlay .btn-toolbar { 60 | position: absolute; 61 | margin: 0; 62 | /* display: none;*/ 63 | top: -10px; 64 | } 65 | 66 | /*.fg-field:hover .btn-toolbar { 67 | display: block; 68 | } 69 | */ 70 | .fg-field-overlay .btn-toolbar-right { 71 | right: 5px; 72 | } 73 | 74 | .fg-field-overlay .btn-toolbar-left { 75 | left: 5px; 76 | } 77 | 78 | .fg-field-overlay .btn-toolbar .btn[disabled] { 79 | -moz-opacity: 1.0; 80 | opacity: 1.0; 81 | filter: alpha(opacity=100); 82 | color: #ccc; 83 | cursor: not-allowed; 84 | pointer-events: auto; 85 | } 86 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/edit.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
6 |
7 |
8 |
11 |
12 |
-------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/categories/categories-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgEditPaletteCategoriesController', function($scope, fgConfig) { 2 | 3 | $scope.categories = fgConfig.fields.categories; 4 | 5 | $scope.setCategory = function(name, category) { 6 | $scope.categoryName = name; 7 | $scope.category = category; 8 | }; 9 | 10 | if(!$scope.category) { 11 | //noinspection LoopStatementThatDoesntLoopJS 12 | for (var name in $scope.categories) { 13 | //noinspection JSUnfilteredForInLoop 14 | $scope.setCategory(name, $scope.categories[name]); 15 | break; 16 | } 17 | } 18 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/categories/categories-controller.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-edit-palette-categories-directive', function() { 2 | 3 | var $scope, $controller, fgConfig; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function(_$rootScope_, _$controller_, _fgConfig_) { 10 | 11 | $controller = _$controller_; 12 | $scope = _$rootScope_.$new(); 13 | fgConfig = _fgConfig_; 14 | 15 | }); 16 | 17 | }); 18 | 19 | it('should use category list from configuration', function() { 20 | 21 | // Act 22 | 23 | $controller('fgEditPaletteCategoriesController', { 24 | $scope: $scope, 25 | fgConfig: fgConfig 26 | }); 27 | 28 | // Assert 29 | 30 | expect($scope.categories).toBe(fgConfig.fields.categories); 31 | 32 | }); 33 | 34 | it('should set first category active on init', function() { 35 | 36 | // Arrange 37 | 38 | fgConfig.fields.categories = { 39 | 'Should be active': { active: true }, 40 | 'Should not be active': { active: false } 41 | }; 42 | 43 | // Act 44 | 45 | $controller('fgEditPaletteCategoriesController', { 46 | $scope: $scope, 47 | fgConfig: fgConfig 48 | }); 49 | 50 | // Assert 51 | 52 | expect($scope.categoryName).toBe('Should be active'); 53 | expect($scope.category).toBe(fgConfig.fields.categories['Should be active']); 54 | 55 | }); 56 | 57 | it('should change both category object and name', function() { 58 | 59 | // Arrange 60 | 61 | var categoryName = 'The new category'; 62 | var category = { 63 | totalyNew: true 64 | }; 65 | 66 | $controller('fgEditPaletteCategoriesController', { 67 | $scope: $scope, 68 | fgConfig: fgConfig 69 | }); 70 | 71 | // Act 72 | 73 | $scope.setCategory(categoryName, category); 74 | 75 | // Assert 76 | 77 | expect($scope.categoryName).toBe(categoryName); 78 | expect($scope.category).toBe(category); 79 | 80 | }); 81 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/categories/categories-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEditPaletteCategories', function () { 2 | return { 3 | templateUrl: 'angular-form-gen/edit/palette/categories/categories.ng.html', 4 | require: '^fgEditPalette', 5 | scope: { 6 | category: "=?" 7 | }, 8 | controller: 'fgEditPaletteCategoriesController' 9 | }; 10 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/categories/categories-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-edit-palette-categories-directive', function() { 2 | 3 | var $scope, $compile; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function(_$rootScope_, _$compile_) { 10 | 11 | $compile = _$compile_; 12 | $scope = _$rootScope_.$new(); 13 | 14 | }); 15 | 16 | }); 17 | 18 | it('should compile template', function() { 19 | 20 | // Arrange 21 | 22 | var template = '
'; 23 | var $element = angular.element(template); 24 | 25 | // Act 26 | 27 | $compile($element)($scope); 28 | $scope.$digest(); 29 | 30 | // Assert 31 | 32 | expect($element.find('.fg-edit-palette-categories').length).toBe(1); 33 | }); 34 | 35 | it('should expose the selected category', function() { 36 | 37 | // Arrange 38 | 39 | var template = '
'; 40 | var $element = angular.element(template); 41 | 42 | // Act 43 | 44 | $compile($element)($scope); 45 | $scope.$digest(); 46 | 47 | // Assert 48 | 49 | expect($scope.selectedCategory).toBeDefined(); 50 | }); 51 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/categories/categories.ng.html: -------------------------------------------------------------------------------- 1 | Palette 3 | - {{ categoryName || 'All field types' }} 4 | 15 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/palette-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgEditPaletteController', function ($scope, fgConfig) { 2 | 3 | $scope.templates = []; 4 | 5 | var tmpls = fgConfig.fields.templates; 6 | var i = tmpls.length; 7 | 8 | while(i--) { 9 | var tmpl = tmpls[i]; 10 | 11 | if(tmpl.editor && tmpl.editor.visible == false) { 12 | continue; 13 | } 14 | 15 | $scope.templates.unshift(angular.copy(tmpl)); 16 | } 17 | 18 | $scope.templateFilter = function (template) { 19 | return !$scope.selectedCategory || $scope.selectedCategory[template.type]; 20 | }; 21 | 22 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/palette-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgEditPalette',function () { 2 | return { 3 | require: ['^fgSchema'], 4 | templateUrl: 'angular-form-gen/edit/palette/palette.ng.html', 5 | controller: 'fgEditPaletteController', 6 | link: function($scope, $element, $attrs, ctrls) { 7 | $scope.schemaCtrl = ctrls[0]; 8 | } 9 | }; 10 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/palette.css: -------------------------------------------------------------------------------- 1 | 2 | /*.fg-edit-palette .controls {*/ 3 | /*margin-left: 0;*/ 4 | /*width: 100%;*/ 5 | /*}*/ 6 | 7 | /*.fg-edit-palette .control-label {*/ 8 | /*float: none;*/ 9 | /*width: 100%;*/ 10 | /*padding-top: 0;*/ 11 | /*text-align: left;*/ 12 | /*}*/ 13 | 14 | /*.fg-edit-palette .fg-field-inner > div {*/ 15 | /*margin-left: 0;*/ 16 | /*width: 100%;*/ 17 | /*float: none;*/ 18 | /*}*/ 19 | 20 | .fg-edit-palette .form-group { 21 | margin: 0px; 22 | } 23 | .fg-field-drag { 24 | opacity: 0.8; 25 | } 26 | 27 | .fg-edit-palette legend { 28 | position: relative; 29 | cursor: pointer; 30 | } 31 | 32 | .fg-edit-palette legend .caret { 33 | border-top: 4px solid #999; 34 | } 35 | 36 | .fg-edit-palette legend:hover { 37 | color: #333; 38 | } 39 | 40 | .fg-edit-palette legend:hover .caret { 41 | border-top: 4px solid #333; 42 | } 43 | 44 | .fg-edit-palette legend .dropdown-menu { 45 | font-size: 14px; 46 | line-height: 20px; 47 | right: auto; 48 | left: auto; 49 | } 50 | 51 | 52 | /*@media (min-width: 768px) and (max-width: 979px) {*/ 53 | /*.fg-edit-palette select,*/ 54 | /*.fg-edit-palette input,*/ 55 | /*.fg-edit-palette textarea {*/ 56 | /*width: 190px;*/ 57 | /*}*/ 58 | 59 | /*.fg-edit-palette input[type="checkbox"],*/ 60 | /*.fg-edit-palette input[type="button"],*/ 61 | /*.fg-edit-palette input[type="radio"] {*/ 62 | /*width: auto;*/ 63 | /*}*/ 64 | /*}*/ 65 | -------------------------------------------------------------------------------- /src/angular-form-gen/edit/palette/palette.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
7 |
8 |
9 | 13 |
14 |
15 |
19 |
20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/checkbox.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 10 |
11 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/checkboxlist-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgCheckboxlist', function() { 2 | 3 | function validateRequired(validation, value, options) { 4 | 5 | var required = validation ? validation.required : false; 6 | 7 | // Set in field-templates/default/checkboxlist.ng.html 8 | 9 | if(required) { 10 | 11 | // Ensures that at least one option is checked 12 | 13 | var x = options.length; 14 | 15 | while(x--) { 16 | if(value[options[x].value]) { 17 | return true; 18 | } 19 | } 20 | 21 | return false; 22 | } 23 | 24 | return true; 25 | 26 | } 27 | 28 | function selectionCount(value) { 29 | var c = 0; 30 | 31 | for(var k in value) { 32 | if(value[k]) { 33 | c += 1; 34 | } 35 | } 36 | 37 | return c; 38 | } 39 | 40 | return { 41 | require: ['^fgField'], 42 | link: function($scope, $element, $attrs, $ctrls) { 43 | 44 | var field = $ctrls[0].field(); 45 | 46 | var formData = $scope.form.data, schema = field.schema; 47 | 48 | $scope.$watchCollection(function() { 49 | return formData[schema.name]; 50 | }, function(value, oldValue) { 51 | 52 | // Ensure that the field is marked as dirty on changes 53 | if(!field.state.$dirty && value !== oldValue) { 54 | field.state.$setViewValue(value); 55 | } 56 | 57 | if(schema.validation) { 58 | var required = validateRequired(schema.validation, value, schema.options); 59 | field.state.$setValidity('required', required); 60 | 61 | var minc = schema.validation.minoptions; 62 | var maxc = schema.validation.maxoptions; 63 | 64 | var min = true, max = true; 65 | 66 | if(minc || maxc) { 67 | var c = selectionCount(value); 68 | 69 | if(minc) { 70 | min = c >= schema.validation.minoptions; 71 | } 72 | 73 | if(maxc) { 74 | max = c <= schema.validation.maxoptions; 75 | } 76 | } 77 | 78 | field.state.$setValidity('minoptions', min); 79 | field.state.$setValidity('maxoptions', max); 80 | } 81 | }); 82 | } 83 | }; 84 | }); 85 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/checkboxlist.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/dropdownlist.ng.html: -------------------------------------------------------------------------------- 1 |
12 |
13 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/email.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/not-in-cache.ng.html: -------------------------------------------------------------------------------- 1 |
2 |

No template registered in cache for field type "{{ field.type }}".

3 |
-------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/number.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/password.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/radiobuttonlist.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 12 |
13 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/selectlist-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgSelectlist', function($timeout) { 2 | 3 | // Angular adds a '? undefined:undefined ?' option dom element if it cannot find a matching model value in the 4 | // options list. Somehow this also happens if the value is in the option list. This directive simply removes 5 | // the invalid option from the dom. 6 | 7 | // https://github.com/angular/angular.js/issues/1019 8 | // http://stackoverflow.com/questions/12654631/why-does-angularjs-include-an-empty-option-in-select 9 | 10 | return { 11 | priority: 1000, 12 | link: function($scope, $element) { 13 | 14 | // Ensure that the ng-repeat has finished by suspending the remove. 15 | 16 | $timeout(function() { 17 | 18 | var $options = $element.find('option'); 19 | var i = $options.length; 20 | 21 | while(--i >= 0) { 22 | var $option = angular.element($options[i]); 23 | if($option.val() == '? undefined:undefined ?') { 24 | $option.remove(); 25 | break; 26 | } 27 | } 28 | }, 0); 29 | } 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/selectlist.ng.html: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/text.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/default/textarea.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/checkbox.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 9 |
10 |
11 |
12 | 13 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/checkboxlist.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 10 |
11 |
13 | 20 |
21 | 22 |
23 |
24 |
25 |
26 | 27 |
28 |
30 | 37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 | 45 |
46 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/dropdownlist.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/email.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
4 |
5 | 12 |
13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/number.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
4 | 5 |
6 | 16 |
17 |
18 | 19 |
20 | 21 | 22 |
23 |
25 | 32 |
33 | 34 |
35 |
36 |
37 |
38 | 39 | 40 |
41 |
43 | 50 |
51 | 52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/password.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
4 |
5 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/radiobuttonlist.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/selectlist.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/text.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
4 | 13 |
14 | 21 |
22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /src/angular-form-gen/field-templates/properties/textarea.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
4 |
5 | 12 |
13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/field/field-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgFieldController', function($scope, fgUtils) { 2 | 3 | var self = this; 4 | var _form, _field; 5 | 6 | this.init = function(fgFormCtrl, fieldSchema, editMode) { 7 | 8 | self.initForm(fgFormCtrl); 9 | self.initField(fieldSchema); 10 | self.initDefaultData(fieldSchema, editMode); 11 | 12 | $scope.form = _form; 13 | $scope.field = _field; 14 | 15 | }; 16 | 17 | this.initForm = function(fgFormCtrl) { 18 | _form = fgFormCtrl ? fgFormCtrl.model : {}; 19 | 20 | return _form; 21 | }; 22 | 23 | this.initField = function(fieldSchema) { 24 | 25 | _field = { 26 | $_id: 'id' + fgUtils.getUnique(), 27 | schema: fieldSchema 28 | }; 29 | 30 | $scope.$watch('field.schema.name', function(value, oldValue) { 31 | self.registerState(value); 32 | }); 33 | 34 | return _field; 35 | }; 36 | 37 | this.initDefaultData = function(fieldSchema, editMode) { 38 | 39 | var fieldName = fieldSchema.name; 40 | 41 | _form.data = _form.data || {}; 42 | 43 | if (editMode) { 44 | 45 | $scope.$watch('field.schema.value', function(value) { 46 | _form.data[fieldSchema.name] = value; 47 | }); 48 | 49 | $scope.$watch('field.schema.name', function(value, oldValue) { 50 | if(value !== oldValue) { 51 | var data = _form.data[oldValue]; 52 | delete _form.data[oldValue]; 53 | _form.data[value] = data; 54 | } 55 | }); 56 | 57 | } else if (_form.data && _form.data[fieldName] === undefined && fieldSchema.value !== undefined) { 58 | _form.data[fieldName] = angular.copy(fieldSchema.value); 59 | } 60 | 61 | return _form.data; 62 | }; 63 | 64 | this.setFieldState = function(state) { 65 | // Called by the field-input directive 66 | _field.state = state; 67 | self.registerState(_field.schema.name); 68 | }; 69 | 70 | this.registerState = function(fieldName) { 71 | // Re-register the ngModelCtrl with the form controller 72 | // whenever the name of the field has been modified. 73 | 74 | if (_form.state && _field.state) { 75 | _form.state.$removeControl(_field.state); 76 | _field.state.$name = fieldName; 77 | _form.state.$addControl(_field.state); 78 | } 79 | 80 | _field.name = fieldName; 81 | 82 | }; 83 | 84 | this.field = function() { 85 | return _field; 86 | }; 87 | 88 | this.form = function() { 89 | return _form; 90 | }; 91 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/field/field-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgField', function(fgFieldLinkFn) { 2 | 3 | return { 4 | require: ['^?fgForm', 'fgField'], 5 | replace: true, 6 | templateUrl: 'angular-form-gen/form/field/field.ng.html', 7 | scope: { 8 | fieldSchema: '=fgField', // The schema definition of the field 9 | tabIndex: '=?fgTabIndex', // Optional tab index -- used in overlay mode to disable focus 10 | editMode: '=?fgEditMode', // Indicates edit mode, which will sync the fieldSchema.value 11 | // to the form data for WYSIWYG pleasures. 12 | noValidationSummary: '=fgNoValidationSummary' // If true hides the validation summary 13 | }, 14 | controller: 'fgFieldController', 15 | link: fgFieldLinkFn 16 | }; 17 | 18 | }).factory('fgFieldLinkFn', function(fgUtils) { 19 | return function($scope, $element, $attrs, ctrls) { 20 | 21 | var fgFormCtrl = ctrls[0]; 22 | var fgFieldCtrl = ctrls[1]; 23 | 24 | if ($scope.tabIndex === undefined) { 25 | $scope.tabIndex = 'auto'; 26 | } 27 | 28 | $scope.renderInfo = fgUtils.getRenderInfo($scope.fieldSchema); 29 | 30 | fgFieldCtrl.init(fgFormCtrl, $scope.fieldSchema, $scope.editMode); 31 | }; 32 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/field/field-focus-directive.js: -------------------------------------------------------------------------------- 1 | //fg.directive('fgFieldFocus', function($parse) { 2 | // return { 3 | // require: ['?^fgForm'], 4 | // link: function($scope, $element, $attrs, ctrls) { 5 | // 6 | // var formCtrl = ctrls[0]; 7 | // 8 | // // if(formCtrl && formCtrl.editMode) { 9 | // // return; 10 | // // } 11 | // 12 | // var e = $element[0]; 13 | // 14 | // var getModel = $parse($attrs.fgFieldFocus); 15 | // var setModel = getModel.assign; 16 | // 17 | // $scope.$watch(getModel, function(value) { 18 | // 19 | // if (value) { 20 | // if(formCtrl) { 21 | // formCtrl.clearFocusOnFields(); 22 | // setModel($scope, true); 23 | // 24 | // if(formCtrl.editMode) { 25 | // return; 26 | // } 27 | // } 28 | // 29 | // e.focus(); 30 | // 31 | // } else if(formCtrl && !formCtrl.editMode) { 32 | // 33 | // e.blur(); 34 | // 35 | // } 36 | // }); 37 | // 38 | // // function onBlur() { 39 | // // // if(getModel($scope) !== undefined) { 40 | // // // $timeout(function() { 41 | // // // setModel($scope, false); 42 | // // // }); 43 | // // // } 44 | // // } 45 | // 46 | // // function onFocus() { 47 | // // $timeout(function() { 48 | // // setModel($scope, true); 49 | // // }); 50 | // // } 51 | // 52 | // // $element.on('focus', onFocus); 53 | // // $element.on('blur', onBlur); 54 | // 55 | // // $scope.$on('$destroy', function() { 56 | // // $element.off('focus', onFocus); 57 | // // $element.off('blur', onBlur); 58 | // // }); 59 | // } 60 | // }; 61 | //}); 62 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/field/field-input-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgFieldInput', function(fgFieldInputLinkFn) { 2 | return { 3 | require: ['^fgField', 'ngModel'], 4 | link: fgFieldInputLinkFn 5 | }; 6 | }).factory('fgFieldInputLinkFn', function() { 7 | return function($scope, $element, $attrs, ctrls) { 8 | 9 | var fgFieldCtrl = ctrls[0]; 10 | var ngModelCtrl = ctrls[1]; 11 | 12 | fgFieldCtrl.setFieldState(ngModelCtrl); 13 | }; 14 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/field/field-input-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-input-directive', function() { 2 | 3 | var fgFieldInputLinkFn, $scope, fieldCtrl = {}, modelCtrl = {}; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function(_fgFieldInputLinkFn_, _$rootScope_){ 10 | fgFieldInputLinkFn = _fgFieldInputLinkFn_; 11 | $scope = _$rootScope_.$new; 12 | }); 13 | 14 | }); 15 | 16 | it('should register the state on the field controller', function() { 17 | 18 | // Arrange 19 | 20 | fieldCtrl.setFieldState = jasmine.createSpy('setFieldState'); 21 | 22 | var ctrls = [ 23 | fieldCtrl, modelCtrl 24 | ]; 25 | 26 | // Act 27 | 28 | fgFieldInputLinkFn($scope, null, null, ctrls); 29 | 30 | // Assert 31 | 32 | expect(fieldCtrl.setFieldState).toHaveBeenCalledWith(modelCtrl); 33 | }); 34 | 35 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/field/field.ng.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
-------------------------------------------------------------------------------- /src/angular-form-gen/form/form-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgFormController', function($scope, $parse) { 2 | 3 | this.model = {}; 4 | var self = this; 5 | 6 | this.init = function(dataExpression, schema, state, editMode) { 7 | // Called by the directive 8 | 9 | self.editMode = editMode; 10 | 11 | var dataGetter = $parse(dataExpression); 12 | var dataSetter = dataGetter.assign; 13 | 14 | $scope.$watch(dataGetter, function(value) { 15 | if(value === undefined) { 16 | value = {}; 17 | 18 | if(dataSetter) { 19 | dataSetter($scope, value); 20 | } 21 | } 22 | 23 | self.model.data = value; 24 | }); 25 | 26 | $scope.$watch(function() { 27 | return schema.model(); 28 | }, function(value) { 29 | if(value === undefined) { 30 | schema.model({}); 31 | } else { 32 | self.model.schema = value; 33 | } 34 | }); 35 | 36 | self.model.state = state; 37 | 38 | 39 | return self.model; 40 | }; 41 | 42 | // this.clearFocusOnFields = function() { 43 | // angular.forEach(self.model.schema.fields, function(field) { 44 | // field.focus = false; 45 | // }); 46 | // }; 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/form-controller.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-form-controller', function() { 2 | 3 | var $scope, $controller; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function(_$rootScope_, _$controller_) { 10 | $scope = _$rootScope_.$new(); 11 | $controller = _$controller_; 12 | }); 13 | 14 | }); 15 | 16 | it('should construct controller', function() { 17 | 18 | var controller = $controller('fgFormController', { 19 | $scope: $scope 20 | }); 21 | 22 | expect(controller).toBeDefined(); 23 | 24 | }); 25 | 26 | it('should create model object', function() { 27 | 28 | var controller = $controller('fgFormController', { 29 | $scope: $scope 30 | }); 31 | 32 | expect(controller.model).toBeDefined(); 33 | 34 | }); 35 | 36 | describe('updateFormModel', function() { 37 | 38 | it('should construct form model', function() { 39 | 40 | // Arrange 41 | 42 | $scope.data = {}; 43 | var schema = {}; 44 | var state = {}; 45 | 46 | var schemaCtrl = { model: function() { return schema }}; 47 | 48 | var controller = $controller('fgFormController', { 49 | $scope: $scope 50 | }); 51 | 52 | // Act 53 | 54 | controller.init('data', schemaCtrl, state); 55 | $scope.$digest(); 56 | 57 | // Assert 58 | 59 | expect(controller.model).toBeDefined(); 60 | expect(controller.model.data).toBe($scope.data); 61 | expect(controller.model.schema).toBe(schema); 62 | expect(controller.model.state).toBe(state); 63 | }); 64 | 65 | }); 66 | 67 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/form-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgForm', function(fgFormCompileFn) { 2 | return { 3 | restrict: 'AE', 4 | require: ['^?form', 'fgForm', '^fgSchema'], 5 | controller: 'fgFormController', 6 | scope: true, 7 | compile: fgFormCompileFn 8 | }; 9 | }).factory('fgFormLinkFn', function() { 10 | return function link($scope, $element, $attrs, ctrls) { 11 | 12 | var ngFormCtrl = ctrls[0]; 13 | var formCtrl = ctrls[1]; 14 | var schemaCtrl = ctrls[2]; 15 | 16 | var editMode = $attrs.fgNoRender === 'true'; 17 | 18 | formCtrl.init($attrs.fgFormData, schemaCtrl, ngFormCtrl, editMode); 19 | 20 | }; 21 | }).factory('fgFormCompileFn', function(fgFormLinkFn) { 22 | return function($element, $attrs) { 23 | 24 | $element.addClass('fg-form'); 25 | 26 | var noRender = $attrs.fgNoRender; 27 | 28 | if (noRender !== 'true') { 29 | var renderTemplate = '
'; 30 | $element.append(renderTemplate); 31 | } 32 | 33 | return fgFormLinkFn; 34 | }; 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/form-fields/form-fields-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgFormFields', function() { 2 | 3 | return { 4 | require: ['^?fgForm'], 5 | restrict: 'AE', 6 | templateUrl: 'angular-form-gen/form/form-fields/form-fields.ng.html', 7 | scope: {}, 8 | link: function($scope, $element, $attrs, ctrls) { 9 | 10 | var fgForm = ctrls[0]; 11 | 12 | $scope.$watch(function() { 13 | return fgForm.model; 14 | }, function(value) { 15 | $scope.form = value; 16 | }); 17 | } 18 | }; 19 | 20 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/form-fields/form-fields-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-form-fields-directive', function() { 2 | 3 | var $compile, $scope; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function(_$compile_, _$rootScope_) { 10 | $compile = _$compile_; 11 | $scope = _$rootScope_.$new(); 12 | }); 13 | }); 14 | 15 | it('should render the directive', function() { 16 | 17 | // Arrange 18 | 19 | var form = { 20 | schema: {} 21 | }; 22 | 23 | $scope.form = form; 24 | 25 | var $element = angular.element('
'); 26 | $element.data('$fgFormController', {}); 27 | 28 | // Act 29 | 30 | var result = $compile($element)($scope); 31 | $scope.$digest(); 32 | 33 | // Assert 34 | 35 | expect(result.find('.fg-form-fields').length).toBe(1); 36 | }); 37 | 38 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/form-fields/form-fields.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .form .control-label, .fg-edit-palette .control-label { 5 | float: none; 6 | width: 100%; 7 | text-align: left; 8 | padding: 0; 9 | } 10 | 11 | .form .fg-field-inner > div, .form .fg-property-field > div, .fg-edit-palette .fg-field-inner > div { 12 | margin-left: 0; 13 | width: 100%; 14 | float: none; 15 | padding: 0; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/form-fields/form-fields.ng.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/form.css: -------------------------------------------------------------------------------- 1 | .fg-field-inner .controls > div > .radio:first-child, 2 | .fg-field-inner .controls > div > .checkbox:first-child { 3 | padding-top: 5px; 4 | } 5 | 6 | .fg-form .radio, .fg-form .checkbox { 7 | margin-top: 0px; 8 | margin-bottom: 5px; 9 | } 10 | 11 | .form-horizontal .fg-form .radio, .form-horizontal .fg-form .checkbox { 12 | margin-bottom: 0px; 13 | } 14 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/schema/schema-controller.js: -------------------------------------------------------------------------------- 1 | fg.controller('fgSchemaController', function($scope, fgUtils) { 2 | 3 | var _model; 4 | 5 | this.model = function(value) { 6 | if(value !== undefined) { 7 | _model = value; 8 | 9 | if(!angular.isArray(value.fields)) { 10 | value.fields = []; 11 | } 12 | } 13 | 14 | return _model; 15 | }; 16 | 17 | this.addField = function(field, index) { 18 | 19 | var copy = fgUtils.copyField(field); 20 | 21 | index = index === undefined ? _model.fields.length : index; 22 | _model.fields.splice(index, 0, copy); 23 | 24 | }; 25 | 26 | this.removeField = function(index) { 27 | _model.fields.splice(index, 1); 28 | }; 29 | 30 | this.swapFields = function(idx1, idx2) { 31 | if (idx1 <= -1 || idx2 <= -1 || idx1 >= _model.fields.length || idx2 >= _model.fields.length) { 32 | return; 33 | } 34 | 35 | _model.fields[idx1] = _model.fields.splice(idx2, 1, _model.fields[idx1])[0]; 36 | }; 37 | 38 | this.moveField = function(fromIdx, toIdx) { 39 | if (fromIdx >= 0 && toIdx <= _model.fields.length && fromIdx !== toIdx) { 40 | var field = _model.fields.splice(fromIdx, 1)[0]; 41 | if (toIdx > fromIdx)--toIdx; 42 | _model.fields.splice(toIdx, 0, field); 43 | } 44 | }; 45 | 46 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/form/schema/schema-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgSchema', function(fgSchemaLinkFn) { 2 | 3 | return { 4 | require: ['fgSchema'], 5 | controller: 'fgSchemaController', 6 | link: fgSchemaLinkFn 7 | }; 8 | 9 | }).factory('fgSchemaLinkFn' , function() { 10 | return function($scope, $element, $attrs, ctrls) { 11 | var schemaCtrl = ctrls[0]; 12 | 13 | $scope.$watch($attrs.fgSchema, function(value) { 14 | schemaCtrl.model(value); 15 | }); 16 | 17 | }; 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /src/angular-form-gen/form/schema/schema-directive.test.js: -------------------------------------------------------------------------------- 1 | describe('fg-schema-directive', function() { 2 | 3 | var $compile, $scope; 4 | 5 | beforeEach(function() { 6 | 7 | module('fg'); 8 | 9 | inject(function(_$rootScope_, _$compile_) { 10 | 11 | $compile = _$compile_; 12 | $scope = _$rootScope_.$new(); 13 | 14 | }); 15 | 16 | }); 17 | 18 | it('should compile directive', function() { 19 | 20 | // Arrange 21 | 22 | var template = '
'; 23 | 24 | var $fixture = angular.element(template); 25 | //$fixture.data('$fgSchemaController', schemaCtrlMock); 26 | 27 | // Act 28 | 29 | $compile($fixture)($scope); 30 | $scope.$digest(); 31 | 32 | // Assert 33 | 34 | expect($fixture.hasClass('ng-scope')).toBe(true); 35 | 36 | }); 37 | 38 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/validation/summary-directive.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgValidationSummary', function(fgValidationSummaryLinkFn) { 2 | 3 | return { 4 | require: ['^?fgField', '^?form'], 5 | templateUrl: 'angular-form-gen/validation/summary.ng.html', 6 | scope: { 7 | fieldName: '@?fgValidationSummary', 8 | validationMessages: '=?fgValidationMessages' 9 | }, 10 | link: fgValidationSummaryLinkFn 11 | }; 12 | }).factory('fgValidationSummaryLinkFn', function(fgConfig) { 13 | 14 | return function($scope, $element, $attrs, ctrls) { 15 | 16 | var fgFieldCtrl = ctrls[0]; 17 | var ngFormController = ctrls[1]; 18 | 19 | if (fgFieldCtrl) { 20 | // Grab the whole field state from the field controller 21 | $scope.field = fgFieldCtrl.field(); 22 | $scope.form = fgFieldCtrl.form(); 23 | 24 | } else if (ngFormController) { 25 | 26 | $scope.form = { 27 | state: ngFormController 28 | }; 29 | 30 | $scope.$watch('fieldName', function(value) { 31 | $scope.field = { 32 | name: value, 33 | state: ngFormController[value] 34 | }; 35 | }); 36 | } 37 | 38 | // Whenever the form designer edits a custom message but decides to delete it later a "" is leftover. 39 | // I don't feel like setting all kinds of watchers so we'll fix that here 40 | 41 | if($scope.validationMessages) { 42 | angular.forEach($scope.validationMessages, function(value, key) { 43 | if(!value) { 44 | delete $scope.validationMessages[key]; 45 | } 46 | }); 47 | } 48 | 49 | $scope.messages = angular.extend({}, fgConfig.validation.messages, $scope.validationMessages); 50 | }; 51 | 52 | }); -------------------------------------------------------------------------------- /src/angular-form-gen/validation/summary.css: -------------------------------------------------------------------------------- 1 | .fg-validation-summary { 2 | margin-top: 10px; 3 | } -------------------------------------------------------------------------------- /src/angular-form-gen/validation/summary.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-form-gen/validation/unique-field-name.js: -------------------------------------------------------------------------------- 1 | fg.directive('fgUniqueFieldName', function () { 2 | 3 | var changeTick = 0; 4 | 5 | function validate(ngModelCtrl, schemaCtrl, field) { 6 | 7 | var schema = schemaCtrl.model(); 8 | var valid = true; 9 | var schemaField; 10 | 11 | if(schema) { 12 | 13 | var fields = schema.fields; 14 | 15 | for (var i = 0; i < fields.length; i++) { 16 | schemaField = fields[i]; 17 | if (schemaField !== field && field.name === schemaField.name) { 18 | valid = false; 19 | break; 20 | } 21 | } 22 | } 23 | 24 | ngModelCtrl.$setValidity('unique', valid); 25 | } 26 | 27 | return { 28 | priority: 100, 29 | require: ['ngModel', '^fgSchema'], 30 | link: function ($scope, $element, $attrs, ctrls) { 31 | 32 | var ngModelCtrl = ctrls[0]; 33 | var schemaCtrl = ctrls[1]; 34 | 35 | var field = $scope.field; 36 | 37 | if(!field) { 38 | throw Error('No field property on scope'); 39 | } 40 | 41 | $scope.$watch(function() { return ngModelCtrl.$modelValue; }, function () { 42 | 43 | // Every instance of this directive will increment changeTick 44 | // whenever the name of the associated field is modified. 45 | 46 | ++changeTick; 47 | }); 48 | 49 | $scope.$watch(function() { return changeTick; }, function() { 50 | 51 | // Every instance of this directive will fire off the validation 52 | // whenever the changeTick has been modifed. 53 | 54 | validate(ngModelCtrl, schemaCtrl, field); 55 | }); 56 | } 57 | }; 58 | }); 59 | -------------------------------------------------------------------------------- /src/angular-logo/angular-logo.css: -------------------------------------------------------------------------------- 1 | 2 | .ng-logo-svg { 3 | width: 100%; 4 | } 5 | 6 | .ng-logo { 7 | height: 180px; 8 | animation: slide-in 1s; 9 | } 10 | 11 | @keyframes slide-in { 12 | 0% { 13 | opacity: 0; 14 | transform: translateX(50%) scale(3,3); 15 | } 16 | } 17 | 18 | .ng-logo-text { 19 | animation-duration: 1s; 20 | animation-name: slide-in-text; 21 | } 22 | 23 | @keyframes slide-in-text { 24 | 0% { 25 | opacity: 0; 26 | transform: translateY(-50%); 27 | } 28 | } -------------------------------------------------------------------------------- /src/angular-logo/angular-logo.js: -------------------------------------------------------------------------------- 1 | 2 | // Demo module 3 | 4 | var ngLogo = angular.module('ngLogo', []); 5 | 6 | ngLogo.directive('ngLogo', function() { 7 | return { templateUrl: 'angular-logo/angular-logo.ng.html' }; 8 | }); 9 | 10 | ngLogo.directive('ngLogoSvg', function() { 11 | return { 12 | replace: true, 13 | templateUrl: 'angular-logo/angular-logo.ng.svg' 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /src/angular-logo/angular-logo.ng.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/angular-seo/angular-seo-templates.js: -------------------------------------------------------------------------------- 1 | // ATTENTION! 2 | // DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY 3 | // SO ALL YOUR CHANGES WILL BE LOST THE NEXT TIME THE FILE IS GENERATED 4 | angular.module('seo').run(['$templateCache', function($templateCache){ 5 | 6 | }]); -------------------------------------------------------------------------------- /src/angular-seo/angular-seo.js: -------------------------------------------------------------------------------- 1 | var seo = angular.module('seo', []); 2 | 3 | seo.config(function ($locationProvider) { 4 | $locationProvider.hashPrefix('!'); 5 | }); 6 | 7 | seo.constant('seoDefaults', { 8 | title: '', 9 | keywords: '', 10 | description: '' 11 | }); 12 | 13 | seo.factory('seoPageState', function(seoDefaults) { 14 | 15 | var state = {}; 16 | 17 | state._reset = function() { 18 | angular.extend(state, seoDefaults); 19 | }; 20 | 21 | state._reset(); 22 | 23 | return state; 24 | 25 | }); 26 | 27 | seo.factory('seoPage', function(seoPageState, $rootScope) { 28 | 29 | function reset() { 30 | seoPageState._reset(); 31 | } 32 | 33 | $rootScope.$on('$routeChangeSuccess', function() { 34 | reset(); 35 | }); 36 | 37 | return { 38 | reset: reset, 39 | title: function(value) { 40 | seoPageState.title = value; 41 | }, 42 | keywords: function(value) { 43 | seoPageState.keywords = value; 44 | }, 45 | description: function(value) { 46 | seoPageState.description = value; 47 | }, 48 | addKeywords: function(value) { 49 | seoPageState.keywords += ', ' + value; 50 | } 51 | }; 52 | }); 53 | 54 | function stateDirective(name) { 55 | 56 | var directiveName = 'seoPage' + name.charAt(0).toUpperCase() + name.substring(1); 57 | 58 | seo.directive(directiveName, function(seoPage) { 59 | 60 | return { 61 | restrict: 'EA', 62 | link: function($scope, $element, $attrs) { 63 | $attrs.$observe(directiveName, function(value) { 64 | if(value) { 65 | seoPage[name](value); 66 | } 67 | }); 68 | } 69 | }; 70 | 71 | }); 72 | } 73 | 74 | stateDirective('title'); 75 | stateDirective('keywords'); 76 | stateDirective('description'); 77 | 78 | seo.directive('seoPageKeywordsAdd', function(seoPage) { 79 | 80 | return { 81 | restrict: 'EA', 82 | link: function($scope, $element, $attrs) { 83 | $attrs.$observe('seoPageKeywordsAdd', function(value) { 84 | if(value) { 85 | seoPage.addKeywords(value); 86 | } 87 | }); 88 | } 89 | }; 90 | 91 | }); -------------------------------------------------------------------------------- /src/app/app-templates.js: -------------------------------------------------------------------------------- 1 | // ATTENTION! 2 | // DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY 3 | // SO ALL YOUR CHANGES WILL BE LOST THE NEXT TIME THE FILE IS GENERATED 4 | angular.module('app').run(['$templateCache', function($templateCache){ 5 | $templateCache.put('app/core/navbar/navbar.ng.html', '
'); 6 | }]); -------------------------------------------------------------------------------- /src/app/app.js: -------------------------------------------------------------------------------- 1 | var app = angular.module('app', 2 | [ 3 | 'seo', 4 | 'ngSanitize', 5 | 'ngAnimate', 6 | 'ngRoute', 7 | 'ngLogo', 8 | 'githubLogo', 9 | 'fg', 10 | 'inform', 11 | 'inform-exception', 12 | 'inform-http-exception', 13 | 'blockUI', 14 | 'markdown' 15 | ]); 16 | 17 | app.config(function(seoDefaults) { 18 | seoDefaults.title = 'angular-form-gen'; 19 | seoDefaults.description = 'Dynamic form schema generator for AngularJS.'; 20 | seoDefaults.keywords = 'angularjs, angular, form, schema, generator'; 21 | }); 22 | 23 | app.controller('MainCtrl', function ($scope, appMenuItems, seoPageState) { 24 | $scope.pageState = seoPageState; 25 | $scope.menuItems = appMenuItems; 26 | }); 27 | 28 | //app.config(function(fgConfigProvider) { 29 | // 30 | // var numberField = fgConfigProvider.fields.get('number'); 31 | // numberField.editor = { visible: false }; 32 | // 33 | //}); 34 | // 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/app/app.less: -------------------------------------------------------------------------------- 1 | body { 2 | /*padding-top: 59px;*/ 3 | overflow: hidden; 4 | padding-bottom: 100px; 5 | } 6 | 7 | /*.container {*/ 8 | /*max-width: 900px;*/ 9 | /*min-width: 320px;*/ 10 | /*}*/ 11 | 12 | html { 13 | overflow-y: scroll; 14 | } 15 | 16 | @import "demo/demo.less"; 17 | @import "home/home.less"; -------------------------------------------------------------------------------- /src/app/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/McNull/angular-form-gen/61096be66c7e0960a6ade1187b123550836b7025/src/app/assets/favicon.ico -------------------------------------------------------------------------------- /src/app/core/menu-items.js: -------------------------------------------------------------------------------- 1 | /* 2 | [{ 3 | name: name of the item. 4 | url: url of the item. 5 | [visible]: boolean or function which indicates if the item is visible. 6 | [pattern]: regular expression used to indicate if the current item is active in the navigation bar. 7 | }] 8 | */ 9 | 10 | app.constant('appMenuItems', [{ 11 | name: 'Home', 12 | url: '/' 13 | }, { 14 | name: 'Demonstration', 15 | url: '/demo' 16 | }]); 17 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.ng.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/app/core/routing.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | app.route = { 4 | /* { 5 | [name]: name of the route, 6 | [url]: url to map the route to. defaults to /{{name-dash-cased}} 7 | [templateUrl]: url to template. defaults to /app/{{url}}/index.html 8 | } */ 9 | home: { 10 | url: '/', 11 | templateUrl: 'app/home/index.html', 12 | label: 'Home' 13 | }, 14 | about: { 15 | url: '/about' 16 | } 17 | }; 18 | 19 | app.config(function ($routeProvider) { 20 | 21 | angular.forEach(app.route, function(routes, key) { 22 | 23 | if(!angular.isArray(routes)) { 24 | routes.name = routes.name || key[0].toUpperCase() + key.slice(1); 25 | routes = [routes]; 26 | } 27 | 28 | angular.forEach(routes, function (route) { 29 | if (!route.url) { 30 | 31 | if (!route.name) { 32 | throw new Error('Route is missing name and url property. ' + JSON.stringify(route)) 33 | } 34 | 35 | route.url = '/' + app.utils.toDashCased(route.name); 36 | } 37 | 38 | if (!route.templateUrl) { 39 | 40 | var url = route.url; 41 | 42 | if (url[0] != '/') { 43 | url = url + '/'; 44 | } 45 | 46 | if (url[url.length - 1] != '/') { 47 | url += '/'; 48 | } 49 | 50 | route.templateUrl = 'app' + url + 'index.html'; 51 | } 52 | 53 | $routeProvider.when(route.url, route); 54 | 55 | }); 56 | 57 | $routeProvider.otherwise({ 58 | redirectTo: '/' 59 | }); 60 | }); 61 | }); 62 | 63 | -------------------------------------------------------------------------------- /src/app/core/utils.js: -------------------------------------------------------------------------------- 1 | 2 | (function(app) { 3 | 4 | app.utils = { 5 | toDashCased: function (str) { 6 | return str.replace(/([a-z])([A-Z])/g, "$1-$2").split(' ').join('-').toLowerCase(); 7 | }, 8 | indexOf: function(arr, obj) { 9 | 10 | var predicate = function(x) { 11 | return x === obj; 12 | }; 13 | 14 | return app.utils.indexOfMatch(arr, predicate); 15 | 16 | }, 17 | indexOfMatch: function(arr, predicate) { 18 | var i = arr.length; 19 | 20 | while(i--) { 21 | if(predicate(arr[i])) { 22 | return i; 23 | } 24 | } 25 | 26 | return -1; 27 | }, 28 | singleOrDefault: function(arr, predicate) { 29 | var idx = app.utils.indexOfMatch(arr, predicate); 30 | return idx === -1 ? null : arr[idx]; 31 | }, 32 | where: function(arr, predicate) { 33 | 34 | var res = []; 35 | 36 | var i = arr.length; 37 | 38 | while(i--) { 39 | var item = arr[i]; 40 | 41 | if(predicate(item)) { 42 | res.push(item); 43 | } 44 | } 45 | 46 | return res; 47 | } 48 | }; 49 | })(app); 50 | 51 | app.factory('fakeHttpResolve', function($q, $timeout, blockUI) { 52 | 53 | var fakeWaitTime = 500; 54 | 55 | function fakeHttpResolve(data) { 56 | var defer = $q.defer(); 57 | 58 | blockUI.start(); 59 | 60 | $timeout(function () { 61 | 62 | blockUI.stop(); 63 | defer.resolve(data ? angular.copy(data) : undefined); 64 | 65 | }, fakeWaitTime); 66 | 67 | return defer.promise; 68 | } 69 | 70 | return fakeHttpResolve; 71 | }); 72 | -------------------------------------------------------------------------------- /src/app/core/utils.test.js: -------------------------------------------------------------------------------- 1 | 2 | describe('app.utils', function() { 3 | 4 | var app; 5 | 6 | beforeEach(module('app')); 7 | 8 | beforeEach(function() { 9 | 10 | app = angular.module('app'); 11 | 12 | }); 13 | 14 | describe('toDashCased', function() { 15 | 16 | it('should convert camel to dash cased', function() { 17 | 18 | var input = "camelCasedText"; 19 | var expected = "camel-cased-text"; 20 | 21 | var output = app.utils.toDashCased(input); 22 | 23 | expect(output).toBe(expected); 24 | 25 | }); 26 | 27 | it('should convert pascal to dash cased', function() { 28 | 29 | var input = "PascalCasedText"; 30 | var expected = "pascal-cased-text"; 31 | 32 | var output = app.utils.toDashCased(input); 33 | 34 | expect(output).toBe(expected); 35 | 36 | }); 37 | 38 | it('should convert space to dash cased', function() { 39 | 40 | var input = "spaced text here"; 41 | var expected = "spaced-text-here"; 42 | 43 | var output = app.utils.toDashCased(input); 44 | 45 | expect(output).toBe(expected); 46 | 47 | }); 48 | 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /src/app/demo/data/data-list.html: -------------------------------------------------------------------------------- 1 | 2 |
6 | 7 |
8 | 9 |

{{ form.name }} Data Entries

10 |

11 | These are the data entries associated with the form {{ form.name }}. 12 |

13 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 46 | 47 |
{{ field.displayName || field.name }} 24 |
25 | 26 |
27 |
32 |
33 | The form {{ form.name }} has no data entries. Click here to add new data. 34 |
35 |
{{ dataEntry[field.name] }} 41 |
42 | 43 | 44 |
45 |
48 |
49 | 50 |
51 |
52 |
53 |
54 | 55 |
56 | -------------------------------------------------------------------------------- /src/app/demo/data/data-list.js: -------------------------------------------------------------------------------- 1 | app.controller('FormDataListCtrl', function($scope, form, dataEntries, $location, FormData, inform, $window) { 2 | 3 | $scope.form = form; 4 | 5 | $scope.dataEntries = dataEntries; 6 | 7 | $scope.addData = function() { 8 | $location.path('/demo/' + form.id + '/data/0/edit'); 9 | }; 10 | 11 | $scope.editData = function(dataEntry) { 12 | $location.path('/demo/' + form.id + '/data/' + dataEntry.id + '/edit/' ); 13 | }; 14 | 15 | $scope.removeData = function(dataEntry) { 16 | 17 | if ($window.confirm('Are you sure you want to delete the form data?')) { 18 | 19 | FormData.remove(dataEntry).then(function() { 20 | inform.add('Form data has been deleted', { type: 'success' }); 21 | 22 | FormData.query(form).then(function(result) { 23 | $scope.dataEntries = result; 24 | }); 25 | }); 26 | 27 | } 28 | 29 | }; 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/demo/data/edit/data-edit.html: -------------------------------------------------------------------------------- 1 |
5 | 6 |
7 | 8 |

Data Entry

9 | 10 |
11 |
12 | 13 |
14 | {{ form.name }} 15 |

{{ form.description }}

16 | 17 |
20 |
21 | 22 |
23 |
24 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |
37 | 38 | -------------------------------------------------------------------------------- /src/app/demo/data/edit/data-edit.js: -------------------------------------------------------------------------------- 1 | app.controller('FormDataEditCtrl', function ($scope, form, formData, $window, $location, FormData, inform) { 2 | 3 | $scope.form = form; 4 | $scope.form.$state = {}; 5 | $scope.form.$data = angular.copy(formData); 6 | $scope.form.$data.formId = form.id; 7 | 8 | $scope.onClose = function () { 9 | 10 | if($scope.form.$state.$dirty && !$window.confirm('Discard unsaved changes?')) { 11 | return; 12 | } 13 | 14 | $location.path('/demo/' + form.id); 15 | 16 | }; 17 | 18 | $scope.onSave = function() { 19 | 20 | if($scope.form.$state.$valid) { 21 | 22 | FormData.save($scope.form.$data).then(function() { 23 | 24 | inform.add('Form data saved.', { type: 'success' }); 25 | $location.path('/demo/' + form.id); 26 | 27 | }); 28 | 29 | } 30 | 31 | }; 32 | 33 | }); -------------------------------------------------------------------------------- /src/app/demo/demo.less: -------------------------------------------------------------------------------- 1 | 2 | table.app-table-form-list > tbody > tr > td, table.app-table-form-list > tbody > tr > th { 3 | vertical-align: middle; 4 | } 5 | 6 | .app-form-description { 7 | margin-bottom: 20px; 8 | } 9 | 10 | .app-demo-tabs { 11 | margin-top: 30px; 12 | } 13 | 14 | .app-demo-tabs.auto-x .tab-content { 15 | overflow-x: auto; 16 | } 17 | 18 | .app-demo-form-table-btn-group { 19 | width: 120px; 20 | } 21 | 22 | .app-demo-data-table-btn-group { 23 | width: 90px; 24 | } 25 | @media screen and (min-width: 992px) { 26 | .app-demo-form-table-btn-group { 27 | width: 210px; 28 | } 29 | 30 | .app-demo-data-table-btn-group { 31 | width: 150px; 32 | } 33 | } 34 | 35 | .collapse-xs { 36 | display: none; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/app/demo/edit/edit-btn-bar.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 |
-------------------------------------------------------------------------------- /src/app/demo/edit/edit.css: -------------------------------------------------------------------------------- 1 | .app-form-edit { 2 | position: relative; 3 | } 4 | 5 | .app-form-edit-btn-bar { 6 | margin-bottom: 10px; 7 | /*position: absolute;*/ 8 | /*top: 10px;*/ 9 | /*right: 10px;*/ 10 | } 11 | 12 | @media screen and (min-width: 768px){ 13 | .app-form-edit-btn-bar { 14 | position: absolute; 15 | top: 70px; 16 | right: 15px; 17 | } 18 | 19 | .app-form-edit-tabs > .fg-tabs > .tab-content { 20 | min-height: 600px; 21 | } 22 | } -------------------------------------------------------------------------------- /src/app/demo/edit/edit.html: -------------------------------------------------------------------------------- 1 |
5 | 6 |
7 | 8 |

9 | Form Editor 10 |

11 | 12 |

13 | On this example page existing or new forms can be edited or created. 14 |

15 | 16 | 21 | 22 | 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 | -------------------------------------------------------------------------------- /src/app/demo/edit/edit.js: -------------------------------------------------------------------------------- 1 | app.controller('FormEditCtrl', function ($scope, $location, inform, $window, formMetaInfo, form, Form) { 2 | 3 | $scope.form = form; 4 | 5 | // Precreate the (form) $state object here so we can access it later. 6 | 7 | $scope.form.$state = {}; 8 | 9 | // Make the form schema for editing application specific form information available on the scope. 10 | 11 | $scope.formMetaInfo = formMetaInfo; 12 | 13 | // Execute when the `close` button is smashed. 14 | 15 | $scope.onClose = function () { 16 | 17 | // Prompt the user if we've got some unsaved modifications. 18 | 19 | if ($scope.form.$state && $scope.form.$state.$dirty && !$window.confirm('Discard unsaved changes?')) { 20 | return; 21 | } 22 | 23 | $location.path('/demo'); 24 | }; 25 | 26 | $scope.canSave = function () { 27 | return $scope.form.$state.$dirty && $scope.form.$state.$valid; 28 | }; 29 | 30 | $scope.onSave = function () { 31 | 32 | if ($scope.canSave()) { 33 | Form.save($scope.form).then(function() { 34 | inform.add('Form saved', { type: 'success' }); 35 | $location.path('/demo'); 36 | }); 37 | } 38 | }; 39 | 40 | 41 | }); -------------------------------------------------------------------------------- /src/app/demo/edit/form-edit-schema.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
6 |
7 |
8 | 9 |
10 |
11 | 12 | Debug 13 | 14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/app/demo/edit/form-edit.html: -------------------------------------------------------------------------------- 1 |
2 | Form Information 3 |
4 |
8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /src/app/demo/edit/form-preview.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 |
9 | {{ form.name }} 10 |

{{ form.description }}

11 | 12 |
15 |
16 |
17 | 18 |
19 | Form State 20 | 21 | 22 |
    23 |
  • 24 | Valid: {{ preview.state.$valid }} 25 |
  • 26 |
  • 27 | Dirty: {{ preview.state.$dirty }} 28 |
  • 29 |
30 | 31 |
32 |
33 | 34 | 35 |
36 | 37 |
38 |
39 |
40 | 41 |
42 |
43 |
44 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /src/app/demo/form-breadcrumb.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/demo/form-breadcrumb.js: -------------------------------------------------------------------------------- 1 | app.directive('formBreadcrumb', function ($route) { 2 | 3 | return { 4 | templateUrl: 'app/demo/form-breadcrumb.html', 5 | link: function ($scope) { 6 | 7 | var nodes = []; 8 | 9 | nodes.push({ 10 | path: '/', name: 'Home' 11 | }); 12 | 13 | nodes.push({ 14 | path: '/demo', name: 'Demonstration' 15 | }); 16 | 17 | var cl = $route.current.locals; 18 | 19 | if (cl.form) { 20 | 21 | if(cl.form.id) { 22 | nodes.push({ 23 | path: '/demo/' + cl.form.id, name: cl.form.name 24 | }); 25 | } 26 | 27 | if(cl.formData) { 28 | nodes.push({ 29 | path: '/demo/' + cl.form.id + '/data/' + (cl.formData.id || 0) + '/edit', name: 'Data Editor' 30 | }); 31 | } else if ($route.current.label == 'Edit') { 32 | nodes.push({ 33 | path: '/demo/' + cl.form.id + '/edit/', name: 'Form Editor' 34 | }); 35 | } 36 | } 37 | 38 | 39 | $scope.nodes = nodes; 40 | } 41 | }; 42 | 43 | }); -------------------------------------------------------------------------------- /src/app/demo/form-list-ctrl.js: -------------------------------------------------------------------------------- 1 | app.controller('FormListCtrl', function ($scope, Form, forms, $location, $window, inform) { 2 | 3 | $scope.forms = forms; 4 | 5 | $scope.newForm = function () { 6 | $location.path('/demo/0/edit'); 7 | }; 8 | 9 | $scope.editForm = function (form) { 10 | $location.path('/demo/' + form.id + '/edit') ; 11 | }; 12 | 13 | $scope.removeForm = function (form) { 14 | if ($window.confirm('Are you sure you want to delete the form?')) { 15 | 16 | Form.remove(form).then(function() { 17 | inform.add('Form has been deleted', { type: 'success' }); 18 | 19 | Form.query().then(function(result) { 20 | $scope.forms = result; 21 | }); 22 | }); 23 | 24 | } 25 | }; 26 | 27 | $scope.displayDataEntries = function (form) { 28 | $location.path('/demo/' + form.id); 29 | }; 30 | 31 | }); 32 | 33 | -------------------------------------------------------------------------------- /src/app/demo/form-meta-info.js: -------------------------------------------------------------------------------- 1 | // This is schema we'll use for all our forms to fill in the meta information. 2 | 3 | app.value('formMetaInfo', { 4 | "fields": [ 5 | { 6 | "type": "text", 7 | "name": "name", 8 | "displayName": "Form name", 9 | "validation": { 10 | "messages": {}, 11 | "required": true 12 | }, 13 | "value": "", 14 | "tooltip": "Enter the name of the form", 15 | "placeholder": "The name of the form" 16 | }, 17 | { 18 | "type": "textarea", 19 | "name": "description", 20 | "displayName": "Description", 21 | "validation": { 22 | "messages": {} 23 | }, 24 | "placeholder": "A short description of this form", 25 | "tooltip": "Enter a short description" 26 | }, 27 | { 28 | "type": "radiobuttonlist", 29 | "name": "layout", 30 | "displayName": "Layout", 31 | "options": [ 32 | { 33 | "value": "form-horizontal", "text": "Horizontal" 34 | }, 35 | { 36 | "value": "form", "text": "Vertical" 37 | } 38 | ], 39 | "value": "form-horizontal", 40 | "tooltip": "Choose a layout" 41 | } 42 | ] 43 | }); -------------------------------------------------------------------------------- /src/app/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 |
6 | 7 |
8 | 9 |

Demonstration

10 |

11 | These demonstration pages are an example how angular-form-gen can be implemented. 12 | All data is stored localy in the browser session (there's no server backend) and a small artificial delay has been builtin to simulate backend communication. 13 |

14 |

15 | Below are some pre-created example forms. 16 |

17 | 28 |
29 |
30 | 31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 50 | 51 |
Name 35 |
36 | 37 |
38 |
{{ form.name }} 44 |
45 | 46 | 47 | 48 |
49 |
52 |
53 | 54 |
55 |
56 |
57 |
58 | 59 | 60 |
61 | 62 | -------------------------------------------------------------------------------- /src/app/demo/routing.js: -------------------------------------------------------------------------------- 1 | 2 | var getFormById = ['Form', '$route', function(Form, $route) { 3 | return parseInt($route.current.params.id) ? Form.get($route.current.params) : {}; 4 | }]; 5 | 6 | var queryFormDataByFormId = ['FormData', '$route', function(FormData, $route) { 7 | return parseInt($route.current.params.id) ? FormData.query($route.current.params) : []; 8 | }]; 9 | 10 | var getFormDataByDataId = ['FormData', '$route', function(FormData, $route) { 11 | return parseInt($route.current.params.dataId) ? FormData.get($route.current.params) : {}; 12 | }]; 13 | 14 | app.route.forms = [ 15 | { 16 | url: '/demo', 17 | controller: 'FormListCtrl', 18 | templateUrl: '/app/demo/index.html', 19 | resolve: { 20 | forms: ['Form', function(Form) { 21 | return Form.query(); 22 | }] 23 | }, 24 | label: 'Demonstration' 25 | }, 26 | { 27 | url: '/demo/:id/edit', 28 | templateUrl: '/app/demo/edit/edit.html', 29 | controller: 'FormEditCtrl', 30 | resolve: { 31 | form: getFormById 32 | }, 33 | label: 'Edit' 34 | }, 35 | { 36 | url: '/demo/:id/data/:dataId/edit', 37 | templateUrl: '/app/demo/data/edit/data-edit.html', 38 | controller: 'FormDataEditCtrl', 39 | resolve: { 40 | form: getFormById, 41 | formData: getFormDataByDataId 42 | }, 43 | label: 'Edit' 44 | }, 45 | { 46 | url: '/demo/:id', 47 | templateUrl: '/app/demo/data/data-list.html', 48 | controller: 'FormDataListCtrl', 49 | resolve: { 50 | form: getFormById, 51 | dataEntries: queryFormDataByFormId 52 | }, 53 | label: 'Data' 54 | } 55 | ]; 56 | -------------------------------------------------------------------------------- /src/app/demo/table-button.js: -------------------------------------------------------------------------------- 1 | app.directive('appTableButton', function() { 2 | 3 | return { 4 | restrict: 'A', 5 | link: function($scope, $element, $attrs) { 6 | 7 | var icon = ''; 8 | var text = ''; 9 | 10 | $element.attr('type', 'button'); 11 | 12 | if(!$element.hasClass('btn')) { 13 | $element.addClass('btn btn-default'); 14 | } 15 | 16 | $element.html(icon + text); 17 | } 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /src/app/home/home.js: -------------------------------------------------------------------------------- 1 | app.controller('HomeCtrl', function($scope, $http) { 2 | 3 | $http.get('app/README.md').then(function(result) { 4 | 5 | var idx = result.data.indexOf('## Description'); 6 | $scope.readme = result.data.substring(idx); 7 | 8 | }); 9 | 10 | }); -------------------------------------------------------------------------------- /src/app/home/home.less: -------------------------------------------------------------------------------- 1 | @github-logo-anim-speed: 0.2s ease-in; 2 | 3 | .app-header { 4 | overflow: hidden; 5 | 6 | .github { 7 | 8 | position: relative; 9 | 10 | .github-logo { 11 | 12 | transition: transform @github-logo-anim-speed; 13 | 14 | position: absolute; 15 | right: 0px; 16 | left: 0px; 17 | margin-left: auto; 18 | margin-right: auto; 19 | height: 200px; 20 | width: 200px; 21 | // top: -70px; 22 | 23 | svg path { 24 | fill: #333; 25 | transition: fill @github-logo-anim-speed; 26 | } 27 | 28 | &:hover svg path { 29 | fill: #2A6496; 30 | } 31 | } 32 | 33 | .github-icon { 34 | 35 | transition: transform @github-logo-anim-speed; 36 | 37 | > svg { 38 | 39 | width: 60px; 40 | height: 60px; 41 | 42 | > path { 43 | transition: all @github-logo-anim-speed; 44 | fill: #333; 45 | stroke: transparent; 46 | } 47 | } 48 | } 49 | 50 | &:hover { 51 | .github-icon { 52 | transform: scale(4); 53 | 54 | > svg > path { 55 | fill: #fff; 56 | opacity: 0.4; 57 | } 58 | } 59 | 60 | .github-logo { 61 | transform: translate(0, -140px); 62 | } 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/app/home/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |

angular-form-gen

8 |
9 |

Design Bootstrap based form schemas for AngularJS in a drag and drop WYSIWYG environment.

10 | 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
-------------------------------------------------------------------------------- /src/github-logo/github-icon-small.ng.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 15 | 16 | -------------------------------------------------------------------------------- /src/github-logo/github-logo.js: -------------------------------------------------------------------------------- 1 | 2 | var githubLogo = angular.module('githubLogo', []); 3 | 4 | githubLogo.directive('githubIcon', function() { 5 | return { 6 | template: '
' 7 | }; 8 | }); 9 | 10 | githubLogo.directive('githubIconSvg', function() { 11 | return { 12 | replace: true, 13 | templateUrl: 'github-logo/github-icon-small.ng.svg' 14 | }; 15 | }); 16 | 17 | githubLogo.directive('githubLogo', function() { 18 | return { 19 | template: '' 20 | }; 21 | }); 22 | 23 | githubLogo.directive('githubLogoSvg', function() { 24 | return { 25 | replace: true, 26 | templateUrl: 'github-logo/github-logo.ng.svg' 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <%- pkg.name %> 12 | 13 | 14 | 15 | 16 | <% _.forEach(css, function(include) { %> 17 | <% 18 | }); %> 19 | 20 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | <% _.forEach(js, function(include) { %> 45 | <% 46 | }); %> 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/README.md: -------------------------------------------------------------------------------- 1 | # Schema Edit With Preview 2 | 3 | This example demonstrates how easy it is to generate a form preview next to the schema editor. -------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/example.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Schema Editor with Form Preview 5 |

6 |

7 | Because of internet ... 8 |

9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 |
24 | 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 | -------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/form-status.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |
6 | Invalid 7 | Valid 8 |
9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

angular-form-gen

4 |

Design Bootstrap based form schemas for AngularJS in a drag and drop WYSIWYG environment.

5 | 6 | Homepage 7 | - 8 | 9 | GitHub Project 10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/preview.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 |
Example Form
8 |
9 |
10 | 11 | 12 | 13 |
16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |
-------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/script.js: -------------------------------------------------------------------------------- 1 | 2 | var myApp = angular.module('myApp', ['fg', 'ngSanitize', 'markdown']); 3 | 4 | myApp.controller('MyController', function($scope, mySchema) { 5 | 6 | $scope.myForm = { 7 | schema: mySchema 8 | }; 9 | 10 | }); 11 | 12 | myApp.value('mySchema', { 13 | "fields": [ 14 | { 15 | "type": "text", 16 | "name": "firstName", 17 | "displayName": "First name", 18 | "validation": { 19 | "messages": {}, 20 | "required": true 21 | }, 22 | "placeholder": "Enter your first name here", 23 | "tooltip": "Enter your first name here" 24 | }, 25 | { 26 | "type": "text", 27 | "name": "lastName", 28 | "displayName": "Last name", 29 | "validation": { 30 | "messages": {}, 31 | "required": true 32 | }, 33 | "placeholder": "Enter your last name here", 34 | "tooltip": "Enter your last name here" 35 | }, 36 | { 37 | "type": "radiobuttonlist", 38 | "name": "sex", 39 | "displayName": "Sex", 40 | "options": [ 41 | { 42 | "value": "male", 43 | "text": "Male" 44 | }, 45 | { 46 | "value": "female", 47 | "text": "Female" 48 | } 49 | ], 50 | "value": "male" 51 | }, 52 | { 53 | "type": "email", 54 | "name": "email", 55 | "displayName": "Email", 56 | "validation": { 57 | "messages": {} 58 | }, 59 | "placeholder": "Enter your email address here", 60 | "tooltip": "Enter your email address here" 61 | }, 62 | { 63 | "type": "checkboxlist", 64 | "name": "color", 65 | "displayName": "Colors", 66 | "options": [ 67 | { 68 | "value": "red", 69 | "text": "Red" 70 | }, 71 | { 72 | "value": "blue", 73 | "text": "Blue" 74 | }, 75 | { 76 | "value": "green", 77 | "text": "Green" 78 | } 79 | ], 80 | "value": {} 81 | } 82 | ] 83 | }); -------------------------------------------------------------------------------- /src/sandbox/ie11-bug-input-disabled/style.css: -------------------------------------------------------------------------------- 1 | /* Styles go here */ 2 | 3 | body { 4 | padding-bottom: 100px; 5 | } 6 | 7 | html { 8 | overflow-y: scroll; 9 | } 10 | 11 | .needs-some-more-space { 12 | margin-top: 20px; 13 | } -------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/README.md: -------------------------------------------------------------------------------- 1 | # Schema Edit With Preview 2 | 3 | This example demonstrates how easy it is to generate a form preview next to the schema editor. -------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/example.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

4 | Schema Editor with Form Preview 5 |

6 |

7 | Because of internet ... 8 |

9 | 10 |
11 | 12 |
13 |
14 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 | 30 |
31 | -------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/form-status.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |
6 | Invalid 7 | Valid 8 |
9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

angular-form-gen

4 |

Design Bootstrap based form schemas for AngularJS in a drag and drop WYSIWYG environment.

5 | 6 | Homepage 7 | - 8 | 9 | GitHub Project 10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/preview.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 |
Example Form
8 |
9 |
10 | 11 | 12 | 13 |
16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/script.js: -------------------------------------------------------------------------------- 1 | 2 | var myApp = angular.module('myApp', ['fg', 'ngSanitize', 'markdown']); 3 | 4 | myApp.controller('MyController', function($scope, mySchema) { 5 | 6 | $scope.myForm = { 7 | schema: mySchema 8 | }; 9 | 10 | }); 11 | 12 | myApp.value('mySchema', { 13 | "fields": [ 14 | { 15 | "type": "text", 16 | "name": "firstName", 17 | "displayName": "First name", 18 | "validation": { 19 | "messages": {}, 20 | "required": true 21 | }, 22 | "placeholder": "Enter your first name here", 23 | "tooltip": "Enter your first name here" 24 | }, 25 | { 26 | "type": "text", 27 | "name": "lastName", 28 | "displayName": "Last name", 29 | "validation": { 30 | "messages": {}, 31 | "required": true 32 | }, 33 | "placeholder": "Enter your last name here", 34 | "tooltip": "Enter your last name here" 35 | }, 36 | { 37 | "type": "radiobuttonlist", 38 | "name": "sex", 39 | "displayName": "Sex", 40 | "options": [ 41 | { 42 | "value": "male", 43 | "text": "Male" 44 | }, 45 | { 46 | "value": "female", 47 | "text": "Female" 48 | } 49 | ], 50 | "value": "male" 51 | }, 52 | { 53 | "type": "email", 54 | "name": "email", 55 | "displayName": "Email", 56 | "validation": { 57 | "messages": {} 58 | }, 59 | "placeholder": "Enter your email address here", 60 | "tooltip": "Enter your email address here" 61 | }, 62 | { 63 | "type": "checkboxlist", 64 | "name": "color", 65 | "displayName": "Colors", 66 | "options": [ 67 | { 68 | "value": "red", 69 | "text": "Red" 70 | }, 71 | { 72 | "value": "blue", 73 | "text": "Blue" 74 | }, 75 | { 76 | "value": "green", 77 | "text": "Green" 78 | } 79 | ], 80 | "value": {} 81 | } 82 | ] 83 | }); -------------------------------------------------------------------------------- /src/sandbox/plunker/form-preview/style.css: -------------------------------------------------------------------------------- 1 | /* Styles go here */ 2 | 3 | body { 4 | padding-bottom: 100px; 5 | } 6 | 7 | html { 8 | overflow-y: scroll; 9 | } 10 | 11 | .needs-some-more-space { 12 | margin-top: 20px; 13 | } -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-edit/example.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
7 |
9 |
10 |
11 |
12 | 13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-edit/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

angular-form-gen

4 |

Design Bootstrap based form schemas for AngularJS in a drag and drop WYSIWYG environment.

5 | 6 | Homepage 7 | - 8 | 9 | GitHub Project 10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/schema-edit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-edit/script.js: -------------------------------------------------------------------------------- 1 | 2 | var myApp = angular.module('myApp', ['fg', 'ngSanitize', 'markdown']); 3 | 4 | myApp.controller('MyController', function($scope) { 5 | $scope.myForm = {}; 6 | }); -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-edit/style.css: -------------------------------------------------------------------------------- 1 | /* Styles go here */ 2 | 3 | body { 4 | padding-bottom: 100px; 5 | } 6 | 7 | html { 8 | overflow-y: scroll; 9 | } -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/README.md: -------------------------------------------------------------------------------- 1 | ### Form Renderer 2 | 3 | Like the _schema editor_, the form renderer should be a child element of a `form` element. This allows for more customisation, like rendering multiple schemas and/or adding form fields without the need for a pre-generated schema. 4 | 5 | Rendering of a schema is done by the `fg-form` directive: 6 | 7 | ``` 8 |
9 |
12 |
13 |
14 | ``` 15 | 16 | The `fg-form-data` attribute specifies that target object model. This model will receive all the input values of the fields. If for example the schema contains a field with the name `myField`, the value of the input will be stored at `myFormData.myField`. 17 | 18 | Any parent `form` or `ng-form` state will be updated accordantly if any of the validation rules defined in the schema will fail or succeed. 19 | 20 | ``` 21 |
22 | 23 |
26 |
27 | 28 | 29 | 30 | 35 | 36 |
37 | ``` 38 | 39 | #### Form Layout 40 | 41 | The form renderer works with both the `form` and the `form-horizontal` bootstrap classes. -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/example.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 | 9 |
10 | 11 | 12 |
13 |
Example Form
14 |
15 |
16 | 17 |
18 |
19 |

This example form is rendered from an earlier created schema.

20 |
21 |
22 | 23 | 24 | 25 |
28 |
29 | 30 | 31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 | 40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 | 48 |
49 | 50 |
51 | -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/form-status.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |
6 | Invalid 7 | Valid 8 |
9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

angular-form-gen

4 |

Design Bootstrap based form schemas for AngularJS in a drag and drop WYSIWYG environment.

5 | 6 | Homepage 7 | - 8 | 9 | GitHub Project 10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/script.js: -------------------------------------------------------------------------------- 1 | 2 | var myApp = angular.module('myApp', ['fg', 'ngSanitize', 'markdown']); 3 | 4 | myApp.controller('MyController', function($scope, mySchema) { 5 | 6 | // Something to store the input at. 7 | 8 | $scope.myFormData = {}; 9 | 10 | // Expose the schema on the scope. 11 | 12 | $scope.mySchema = mySchema; 13 | 14 | }); 15 | 16 | myApp.value('mySchema', { 17 | "fields": [ 18 | { 19 | "type": "text", 20 | "name": "firstName", 21 | "displayName": "First name", 22 | "validation": { 23 | "messages": {}, 24 | "required": true 25 | }, 26 | "placeholder": "Enter your first name here", 27 | "tooltip": "Enter your first name here" 28 | }, 29 | { 30 | "type": "text", 31 | "name": "lastName", 32 | "displayName": "Last name", 33 | "validation": { 34 | "messages": {}, 35 | "required": true 36 | }, 37 | "placeholder": "Enter your last name here", 38 | "tooltip": "Enter your last name here" 39 | }, 40 | { 41 | "type": "radiobuttonlist", 42 | "name": "sex", 43 | "displayName": "Sex", 44 | "options": [ 45 | { 46 | "value": "male", 47 | "text": "Male" 48 | }, 49 | { 50 | "value": "female", 51 | "text": "Female" 52 | } 53 | ], 54 | "value": "male" 55 | }, 56 | { 57 | "type": "email", 58 | "name": "email", 59 | "displayName": "Email", 60 | "validation": { 61 | "messages": {} 62 | }, 63 | "placeholder": "Enter your email address here", 64 | "tooltip": "Enter your email address here" 65 | }, 66 | { 67 | "type": "checkboxlist", 68 | "name": "color", 69 | "displayName": "Colors", 70 | "options": [ 71 | { 72 | "value": "red", 73 | "text": "Red" 74 | }, 75 | { 76 | "value": "blue", 77 | "text": "Blue" 78 | }, 79 | { 80 | "value": "green", 81 | "text": "Green" 82 | } 83 | ], 84 | "value": {} 85 | } 86 | ] 87 | }); -------------------------------------------------------------------------------- /src/sandbox/plunker/schema-render/style.css: -------------------------------------------------------------------------------- 1 | /* Styles go here */ 2 | 3 | body { 4 | padding-bottom: 100px; 5 | } 6 | 7 | html { 8 | overflow-y: scroll; 9 | } -------------------------------------------------------------------------------- /src/sandbox/plunker/wizard/README.md: -------------------------------------------------------------------------------- 1 | # Wizard Example 2 | 3 | This plunker displays how a custom wizard can be created based on the form schemas created with the 4 | schema editor. 5 | -------------------------------------------------------------------------------- /src/sandbox/plunker/wizard/example.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /src/sandbox/plunker/wizard/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

angular-form-gen

4 |

Design Bootstrap based form schemas for AngularJS in a drag and drop WYSIWYG environment.

5 | 6 | Homepage 7 | - 8 | 9 | GitHub Project 10 | 11 |
12 |
-------------------------------------------------------------------------------- /src/sandbox/plunker/wizard/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/sandbox/plunker/wizard/my-wizard.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | 6 |
7 |
8 | {{ currentPage.name }} 9 |
10 |
11 |
12 | 13 |
14 |
15 |

{{ currentPage.description }}

16 |
17 |
18 | 19 | 20 | 21 |
24 |
25 | 26 | 27 | 28 |
29 | 30 | 31 |
32 | 33 | 34 |
35 |
Summary
36 |
37 |
38 | 39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 | 49 |
50 | 51 |
52 | 55 | 58 | 61 |
62 | 63 |
64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /src/sandbox/plunker/wizard/style.css: -------------------------------------------------------------------------------- 1 | /* Styles go here */ 2 | 3 | body { 4 | padding-bottom: 100px; 5 | } 6 | 7 | html { 8 | overflow-y: scroll; 9 | } 10 | 11 | .needs-some-more-space { 12 | margin-top: 30px; 13 | } 14 | 15 | .form-group .jsonify { 16 | margin-top: 0; 17 | } 18 | 19 | .my-wizard-page-slide, .my-wizard .control-label { 20 | transition: all 0s; 21 | opacity: 0; 22 | } 23 | 24 | .my-wizard-page-slide-left { 25 | margin-left: 200px; 26 | } 27 | 28 | .my-wizard .control-label { 29 | transform: translateX(-100px); 30 | } 31 | 32 | .my-wizard-page-slide-right { 33 | margin-left: -200px; 34 | } 35 | 36 | .my-wizard-page-active .my-wizard-page-slide, .my-wizard-page-active .control-label { 37 | transition: all 1s; 38 | opacity: 1; 39 | } 40 | 41 | .my-wizard-page-active .my-wizard-page-slide-left, 42 | .my-wizard-page-active .my-wizard-page-slide-right { 43 | margin-left: 0px; 44 | } 45 | 46 | .my-wizard-page-active .control-label { 47 | transform: translateX(0px); 48 | } -------------------------------------------------------------------------------- /src/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://angular-form-gen.nullest.com/#!/ 5 | daily 6 | 1.0 7 | 8 | 9 | http://angular-form-gen.nullest.com/#!/demo 10 | daily 11 | 0.5 12 | 13 | 14 | http://angular-form-gen.nullest.com/#!/demo/0/edit 15 | daily 16 | 1.0 17 | 18 | 19 | http://angular-form-gen.nullest.com/#!/demo/0/edit 20 | daily 21 | 1.0 22 | 23 | 24 | http://angular-form-gen.nullest.com/#!/demo/4 25 | daily 26 | 0.5 27 | 28 | 29 | http://angular-form-gen.nullest.com/#!/demo/4/data/0/edit 30 | daily 31 | 1.0 32 | 33 | --------------------------------------------------------------------------------