├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── MIT-LICENSE.txt
├── README.md
├── autocomplete-form-tagit.html
├── bower.json
├── complex-sample.html
├── controls
├── dialog.js
├── dialog.ts
├── jquery.jsform.bsfileUpload.js
├── jquery.responsiveDialog.js
├── jquery.sorTable.js
├── jquery.tree.css
└── jquery.tree.js
├── js
├── jquery.jsForm-1.4.0.js
├── jquery.jsForm-1.5.0.js
├── jquery.jsForm-1.5.1.js
├── jquery.jsForm-1.5.2.js
├── jquery.jsForm-1.5.4.js
├── jquery.jsForm-1.5.5.js
├── jquery.jsForm-1.6.0.js
├── jquery.jsForm.d.ts
├── jquery.jsForm.js
├── jquery.jsForm.min.js
└── jquery.jsForm.min.map
├── jsForm.jquery.json
├── lib
├── handlebars-1.0.rc.1.js
├── jquery.format-1.3.js
├── jquery.min.js
├── jquery.simpledateformat.js
├── jquery.tagit.css
└── tag-it.js
├── package-lock.json
├── package.json
├── sample-conditional.html
├── sample-connect.html
├── sample-dataHandler.html
├── sample-multiselect.html
├── sample-object.html
├── sample.html
├── sortable-editable-sample.html
├── sortable-sample.html
├── src
├── jquery.jsForm.controls.js
├── jquery.jsForm.d.ts
└── jquery.jsForm.js
└── test
├── jquery.jsForm.performance.js
├── jquery.jsForm.test.js
├── performance.jquery.jsForm.html
├── qunit-1.10.0.css
├── qunit-1.10.0.js
└── test.jquery.jsForm.html
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /.project
3 | /.settings
4 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esversion": 11,
3 | "boss": true,
4 | "curly": true,
5 | "eqeqeq": true,
6 | "eqnull": true,
7 | "expr": true,
8 | "immed": true,
9 | "noarg": true,
10 | "onevar": true,
11 | "quotmark": "double",
12 | "smarttabs": true,
13 | "trailing": true,
14 | "undef": true,
15 | "unused": true,
16 |
17 | "node": true
18 | }
19 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function( grunt ) {
2 |
3 | "use strict";
4 |
5 | var readOptionalJSON = function( filepath ) {
6 | var data = {};
7 | try {
8 | data = grunt.file.readJSON( filepath );
9 | } catch(e) {}
10 | return data;
11 | },
12 | srcHintOptions = readOptionalJSON("src/.jshintrc");
13 |
14 | grunt.initConfig({
15 | pkg: grunt.file.readJSON("package.json"),
16 | // dst: readOptionalJSON("dist/.destination.json"),
17 | concat: {
18 | js: {
19 | src: ["src/jquery.jsForm.js", "src/jquery.jsForm.controls.js"],
20 | dest: "js/jquery.jsForm-<%= pkg.version %>.js"
21 | },
22 | js2: {
23 | src: ["src/jquery.jsForm.js", "src/jquery.jsForm.controls.js"],
24 | dest: "js/jquery.jsForm.js"
25 | }
26 |
27 | },
28 | jshint: {
29 | dist: {
30 | src: [ "js/jquery.jsForm-<%= pkg.version %>.js" ],
31 | options: srcHintOptions
32 | },
33 | grunt: {
34 | src: [ "Gruntfile.js" ],
35 | options: {
36 | jshintrc: ".jshintrc"
37 | }
38 | },
39 | options: {
40 | esversion: 11
41 | }
42 | // tests: {
43 | // TODO: Once .jshintignore is supported, use that instead.
44 | // issue located here: https://github.com/gruntjs/grunt-contrib-jshint/issues/1
45 | // src: [ "test/data/{test,testinit,testrunner}.js", "test/unit/**/*.js" ],
46 | // options: {
47 | // jshintrc: "test/.jshintrc"
48 | // }
49 | // }
50 | },
51 | uglify: {
52 | all: {
53 | files: {
54 | "js/jquery.jsForm.min.js": [ "js/jquery.jsForm-<%= pkg.version %>.js" ]
55 | },
56 | options: {
57 | banner: "/*!\n * jQuery.jsForm v<%= pkg.version %> | (c) 2015 <%=pkg.author.name%> <%=pkg.author.url%>\n * Usage: <%=pkg.homepage%>\n */\n",
58 | beautify: {
59 | ascii_only: true
60 | }
61 | }
62 | }
63 | }
64 | });
65 |
66 | // Load grunt tasks from NPM packages
67 | grunt.loadNpmTasks("grunt-compare-size");
68 | grunt.loadNpmTasks("grunt-contrib-jshint");
69 | grunt.loadNpmTasks("grunt-contrib-uglify");
70 | grunt.loadNpmTasks("grunt-contrib-concat");
71 |
72 |
73 | // Short list as a high frequency watch task
74 | grunt.registerTask( "default", [ "concat:js", "concat:js2", "uglify", "jshint" ] );
75 | };
76 |
77 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2013 Niko Berger and Contributors,
2 | http://www.corinis.com
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jquery.jsForm
2 | =============
3 |
4 | jQuery based form library that allows you to handle data within a javascript object (like from a JSON request) with plain html forms.
5 |
6 | This is a library allowing a binding between html and a javascript object as described in [MVVM](http://en.wikipedia.org/wiki/Model_View_ViewModel) similar to other libraries like [knockout.js](http://knockoutjs.com/) or [AngularJs](http://angularjs.org/).
7 | JsForm only takes care of the rendering of data in a html. The controller can be written in standard jQuery. This keeps the library clean and simple.
8 |
9 |
10 | The main features of this library are:
11 |
12 | * Use html markup to fill your forms/page with almost any js object dynamically
13 | * Update an existing js object with changes done within a form (=manipulate data without extra code)
14 | * Provide basic functions for formatting (i.e. date/time, money, byte) using html markup
15 | * Provide form validation functionality
16 | * handle collections (arrays) with subobjects
17 | * handle binaries (blobs) within json by converting them to data url
18 | * provides helper methods to handle array manipulation (add new entry/remove an entry) using only html markup
19 | * Can be used in connection with an autocomplete function to add new array objects
20 | * Compatible with [bootstrap](https://getbootstrap.com/), [jQuery UI](http://jqueryui.com/) and [jQuery Mobile](http://jquerymobile.com/) and other frameworks by working directly on the DOM
21 | * addon library for form controls and layouting (comes bundled in the minified version), internationalisation
22 | * unit tested using [QUnit](http://www.gargan.org/jsform/test/test.jquery.jsForm.html)
23 | * jslint clean
24 | * Minified+Gzipped: 7kb
25 |
26 | You can also check out some [Demos](http://www.gargan.org/jsform/index.jsp).
27 |
28 | # Libraries
29 |
30 | ## Required
31 | * [jQuery](http://jquery.com/) 1.9 or higher
32 | * [luxon](https://moment.github.io/luxon/) Luxon for Date/Time Formatting
33 |
34 | ## Optional
35 |
36 | Optional Libraries are used with jquery.jsForm.controls.js to allow various input methods:
37 |
38 | * [jQuery Format Plugin](http://www.asual.com/jquery/format/) 1.2 or higher- used when working with date/number formats
39 | * [clockpicker](https://weareoutman.github.io/clockpicker/) used to display a clock input using input class="time"
40 | * [flatpickr](https://flatpickr.js.org) date and dateTime control (class="date" or class="dateTime")
41 | * [datetimepicker](https://eonasdan.github.io/bootstrap-datetimepicker/) alternative datetime picker (class="date" or class="dateTime")
42 | * [Moment.js](http://momentjs.com/) allows for finer grained international date support (deprecated) -> use [luxon](https://moment.github.io/luxon/)
43 |
44 | # Download
45 |
46 | Current Version: 1.6.0
47 |
48 | * [Minified](https://github.com/corinis/jsForm/raw/master/js/jquery.jsForm.min.js)
49 | * [Combined Source](https://github.com/corinis/jsForm/raw/master/js/jquery.jsForm.js)
50 |
51 | On bower.io:
52 | ```
53 | bower install jquery-jsform --save
54 | ```
55 |
56 | # Documentation
57 |
58 | can be found in the github wiki:
59 |
60 | * [Dom Layout](https://github.com/corinis/jsForm/wiki/JsForm-Dom-Layout) tells you how you can/should structure your html
61 | * [API Documentation](https://github.com/corinis/jsForm/wiki/JsForm-Documentation) for the javascript options
62 | * [Form Controls and Validation](https://github.com/corinis/jsForm/wiki/Controls) additional formatting/validating controls
63 |
64 | # Custom UI Controls
65 |
66 | Additionally to the base form, I created some custom controls. There might be much more powerful versions out there,
67 | but the aim here is to have simple to use controls that are compatible with the json-approach of jsForm and also
68 | compatible to jquery-ui and bootstrap.
69 |
70 | * [Tree](https://github.com/corinis/jsForm/wiki/Tree)
71 | * [jquery.tree.js](https://raw.github.com/corinis/jsForm/master/controls/jquery.tree.js)
72 | * [jquery.tree.css](https://raw.github.com/corinis/jsForm/master/controls/jquery.tree.css)
73 | * [Sortable Table](https://github.com/corinis/jsForm/wiki/Sortable-Table)
74 | * [jquery.sorTable.js](https://raw.github.com/corinis/jsForm/master/controls/jquery.sorTable.js) also allows quick filtering
75 | * [jqueryUI Responsive Dialog](https://github.com/corinis/jsForm/wiki/Responsive-Dialog)
76 | * [jquery.responsiveDialog.js](https://github.com/corinis/jsForm/wiki/jquery.responsiveDialog.js) Plugin which extends the default jquery.ui dialog to be more responsive
77 |
78 |
79 | # Quickstart
80 |
81 | * Start with a simple html to show you the basic usage [download sample](https://raw.github.com/corinis/jsForm/master/sample.html) [view life](http://www.gargan.org/jsform/index.jsp).
82 | :
83 |
84 | ```html
85 |
86 |
87 |
88 |
89 |
110 |
111 |
112 | Simple Form Test
113 |
135 | Show Object
136 |
137 |
138 | ```
139 |
140 | ## Also check out the other samples:
141 |
142 | [view life demos](http://www.gargan.org/jsform/index.jsp)
143 |
144 | * [complex json with collections within collections](https://raw.github.com/corinis/jsForm/master/complex-sample.html)
145 | * [complex json with collections, sortable and editable](https://raw.github.com/corinis/jsForm/master/sortable-editable-sample.html)
146 | * [select from a given list of objects](https://raw.github.com/corinis/jsForm/master/sample-multiselect.html)
147 | * [show elements based on conditions within the data](https://raw.github.com/corinis/jsForm/master/sample-conditional.html)
148 | * [use the tagit control to select from a list of objects](https://raw.github.com/corinis/jsForm/master/autocomplete-form-tagit.html)
149 |
150 |
--------------------------------------------------------------------------------
/autocomplete-form-tagit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
95 |
96 |
97 | Autocomplete form with tag-it control
98 |
99 | Name:
100 | active
101 |
102 |
103 |
104 |
105 | Show Object
106 |
107 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-jsform",
3 | "version": "1.6.0",
4 | "homepage": "https://github.com/corinis/jsForm",
5 | "authors": [
6 | "Niko "
7 | ],
8 | "repository": {
9 | "type": "git",
10 | "url": "git://github.com/corinis/jsForm.git"
11 | },
12 | "bugs": "https://github.com/corinis/jsForm/issues",
13 | "description": "jQuery based form library that allows you to handle data within a js object (i.e. JSON) with html forms.\nYou can modify all fields/properties by simply creating a html form with the correct naming schema (MVVM Pattern).\n\nThe main features of this library are:\n Full standard html with data available in a js object\n Update an existing js object with changes done within a form\n Fully internationalizable with number format, currency and date formatting\n Provide basic functions for formatting (i.e. date/time, money) using html markup\n Provide form validation functionality\n handle collections (arrays) with subobjects\n provides helper methods to handle array manipulation (add new entry/remove an entry, sorting) using only html markup\n Can be used in connection with an autocomplete function to add new array objects",
14 | "main": [
15 | "js/jquery.jsForm.min.js",
16 | "lib/jquery.format-1.3.js"
17 | ],
18 | "keywords": [
19 | "jquery",
20 | "form",
21 | "json",
22 | "mvvm",
23 | "form2json",
24 | "json2form",
25 | "ajax"
26 | ],
27 | "license": "MIT",
28 | "dependencies": {
29 | "jquery": ">=1.9"
30 | },
31 | "ignore": [
32 | "**/.*",
33 | "*.html",
34 | "*.txt",
35 | "*.json",
36 | "*.md",
37 | "Gruntfile.js",
38 | "node_modules",
39 | "bower_components",
40 | "test",
41 | "src"
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/complex-sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
49 |
50 |
51 | Multi level array testform
52 |
53 | Name:
54 |
active
55 |
56 | visible
57 | important
58 | hidden
59 |
60 |
61 |
62 | Group: groups.name
63 |
66 | up down
67 |
68 |
69 |
70 | Show Object
71 |
72 |
--------------------------------------------------------------------------------
/controls/dialog.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | ///
3 | ///
4 | ///
5 | /**
6 | * Documentation:(https://project.corinis.com/docs/pages/viewpage.action?pageId=99615166)
7 | *
8 | * Dialog Helper Class.
9 | * this works in combination with jqueryUI dialog and Connection Utils
10 | * to provide a generic CRUD dialog functionality
11 | *
12 | * This supports basic Dialogs and sub-dialogs.
13 | *
14 | * uses i18n to define button labels.
15 | * Use with following js:
16 | * ```js
17 | * subDialog = $("#subDialogId");
18 | * Dialog.init($detail, {
19 | * service: "InfoService",
20 | * saveMethod: "persist",
21 | * create: { action: "#addMenuBtn", data: {} }
22 | * update: { action: "#changeMenuBtn", data: ()=>{ return selectedItem; } }
23 | * view: { action: "#changeMenuBtn", data: ()=>{ return selectedItem; } }
24 | * subDialogs: [{
25 | * dlg: subDialog,
26 | * action: ".rawDetail"
27 | * }]
28 | * });
29 | * ```
30 | * Sample HTML:
31 | * ```
32 | *
41 | *
42 | *
46 | *
47 | * ```
48 | * Events
49 | * You can use the default dialog events to add extra actions:
50 | * ```
51 | * $dlg.on("dialogopen", function(){ alert("just opened a dialog"); });
52 | * $dlg.on("success", function(){ alert("just successfully saved"); });
53 | *
54 | * ```
55 | */
56 | var Dialog;
57 | (function (Dialog) {
58 | class DialogConfig {
59 | constructor() {
60 | this.width = 600;
61 | this.responsive = 800;
62 | this.modal = true;
63 | }
64 | }
65 | class ActionConfig {
66 | }
67 | class SubDialogConfig {
68 | constructor() {
69 | this.width = 400;
70 | }
71 | }
72 | /**
73 | * Initializes the dialog overlay with the given ID and options.
74 | *
75 | * @param {any} id - the ID of the dialog
76 | * @param {object} options - optional settings for the dialog
77 | * @return {undefined}
78 | */
79 | function init(id, options, replaceButton = false) {
80 | if (typeof Panel !== "undefined") {
81 | console.log("Using panel init ", id, options);
82 | Panel.init(id, options);
83 | return;
84 | }
85 | if (replaceButton) {
86 | setTimeout(() => {
87 | console.log("Replace button pane...");
88 | replaceDefaultButtonPane($dlg);
89 | }, 100);
90 | }
91 | const $dlg = $(id);
92 | if (!$dlg.length) {
93 | console.log("Unable to find dialog: " + id, new Error().stack);
94 | return;
95 | }
96 | if (!options) {
97 | options = new DialogConfig();
98 | }
99 | if ($dlg.attr("data-modal") === "false") {
100 | options.modal = false;
101 | }
102 | else {
103 | options.modal = true;
104 | }
105 | if ($dlg.attr("data-icon")) {
106 | options.icon = $dlg.attr("data-icon");
107 | }
108 | if ($dlg.attr("data-color")) {
109 | options.color = $dlg.attr("data-color");
110 | }
111 | if ($dlg.attr("data-width")) {
112 | options.width = Number($dlg.attr("data-width"));
113 | }
114 | if ($dlg.attr("data-responsive")) {
115 | options.responsive = Number($dlg.attr("data-responsive"));
116 | }
117 | if ($dlg.attr("data-buttonsave")) {
118 | options.buttonSave = $dlg.attr("data-buttonsave") === "true";
119 | }
120 | /**
121 | * Save the form (and close)
122 | */
123 | let save = function (close) {
124 | if ((options === null || options === void 0 ? void 0 : options.saveMethod) && !$dlg.hasClass("view")) {
125 | let data = $dlg.jsForm("get");
126 | if ($dlg.data().onData) {
127 | data = $dlg.data().onData(data);
128 | }
129 | if (!data) {
130 | alert(i18n.dialog_validation_notOk);
131 | return;
132 | }
133 | if (typeof options.saveMethod === "function") {
134 | options.saveMethod(data, function (data) {
135 | let postResult = null;
136 | if ($dlg.data().onDone) {
137 | postResult = $dlg.data().onDone(data);
138 | }
139 | else if (options.onDone) {
140 | postResult = options.onDone(data);
141 | }
142 | $dlg.trigger("success", [data]);
143 | if (postResult === false || !close) {
144 | $dlg.jsForm("fill", data);
145 | return;
146 | }
147 | $dlg.dialog("close");
148 | });
149 | }
150 | else {
151 | Core.conn.execute(options.service, options.saveMethod, [data]).then(function (data) {
152 | let postResult = null;
153 | if ($dlg.data().onDone) {
154 | postResult = $dlg.data().onDone(data);
155 | }
156 | else if (options.onDone) {
157 | postResult = options.onDone(data);
158 | }
159 | $dlg.trigger("success", [data]);
160 | if (postResult === false || !close) {
161 | $dlg.jsForm("fill", data);
162 | return;
163 | }
164 | $dlg.dialog("close");
165 | });
166 | }
167 | }
168 | else if ($dlg.data().onDone) {
169 | let data = $dlg.jsForm("get");
170 | // prepare data (if required)
171 | if ($dlg.data().onData) {
172 | data = $dlg.data().onData(data);
173 | }
174 | // call the onDone function
175 | let res = $dlg.data().onDone(data, (data) => {
176 | if (close || !data) {
177 | $dlg.dialog("close");
178 | }
179 | else {
180 | $dlg.jsForm("fill", data);
181 | }
182 | });
183 | if (res === false) {
184 | alert(i18n.dialog_validation_notOk);
185 | return;
186 | }
187 | else if (res === true) {
188 | // keep open, wait for callback
189 | }
190 | else {
191 | $dlg.dialog("close");
192 | }
193 | }
194 | else {
195 | $dlg.dialog("close");
196 | }
197 | };
198 | // action integration: open dialog
199 | if (options) {
200 | if (options.create) {
201 | $(options.create.action).click(() => {
202 | if ($(this).hasClass("disabled")) {
203 | return;
204 | }
205 | $dlg.removeClass("view");
206 | Dialog.open($dlg, options.create.data, options.create.onDone, options.create.onData);
207 | });
208 | }
209 | if (options.update) {
210 | $(options.update.action).click(() => {
211 | if ($(this).hasClass("disabled")) {
212 | return;
213 | }
214 | $dlg.removeClass("view");
215 | Dialog.open($dlg, options.update.data, options.update.onDone, options.update.onData);
216 | });
217 | }
218 | if (options.view) {
219 | $(options.view.action).click(() => {
220 | if ($(this).hasClass("disabled")) {
221 | return;
222 | }
223 | $dlg.addClass("view");
224 | Dialog.open($dlg, options.view.data, options.view.onDone, options.view.onData);
225 | });
226 | }
227 | }
228 | let buttons = [];
229 | if (options.buttonSave) {
230 | buttons.push({
231 | 'class': "ui-button-primary",
232 | text: i18n.dialog_save,
233 | responsive: {
234 | html: ' ',
235 | position: 1
236 | },
237 | click: function () {
238 | save(false);
239 | }
240 | });
241 | }
242 | buttons.push({
243 | 'class': "ui-button-primary",
244 | text: i18n.dialog_ok,
245 | responsive: {
246 | html: ' ',
247 | position: 1
248 | },
249 | click: function () {
250 | save(true);
251 | }
252 | });
253 | buttons.push({
254 | text: i18n.dialog_cancel,
255 | click: function () {
256 | $dlg.dialog("close");
257 | }
258 | });
259 | // default buttons
260 | if (options.buttons) {
261 | if (typeof options.buttons === "function")
262 | buttons = options.buttons($dlg, buttons);
263 | else
264 | buttons = options.buttons;
265 | }
266 | // fill default class
267 | if ((buttons === null || buttons === void 0 ? void 0 : buttons.length) > 1) {
268 | buttons.forEach(item => {
269 | if (!item.class && item.color) {
270 | item.class = 'btn btn-' + item.color;
271 | }
272 | });
273 | }
274 | // check if overlay exists
275 | if (options.overlay && $(options.overlay).length === 0) {
276 | // remove overlay (not found)
277 | options.overlay = null;
278 | }
279 | $dlg.dialog({
280 | autoOpen: false,
281 | width: options.width || 600,
282 | modal: options.modal,
283 | titleIcon: {
284 | background: options.color,
285 | icon: options.icon
286 | },
287 | responsive: {
288 | overlay: options.overlay,
289 | limit: options.responsive || options.width || 600,
290 | left: {
291 | 'class': 'bg-icon active ' + options.color,
292 | text: ' ',
293 | click: function () {
294 | // reset changed fields
295 | if ($(this).jsForm) {
296 | $(this).jsForm("resetChanged");
297 | }
298 | $(this).dialog("close");
299 | }
300 | },
301 | center: { 'class': 'has-icon' },
302 | right: true
303 | },
304 | buttons: buttons
305 | });
306 | if (options.subDialogs) {
307 | options.subDialogs.forEach((curDlg, index, array) => {
308 | $(curDlg.action, $dlg).click(function () {
309 | if ($(this).hasClass("disabled")) {
310 | return;
311 | }
312 | curDlg.dlg.dialog("open");
313 | curDlg.dlg.data().updateData = $(this).closest(".POJO").data();
314 | curDlg.dlg.jsForm("fill", $(this).closest(".POJO").data().pojo);
315 | });
316 | let saveDlg = function () {
317 | let data = curDlg.dlg.jsForm("get");
318 | if (!data) {
319 | alert(i18n.dialog_validation_notOk);
320 | return;
321 | }
322 | // update original object
323 | curDlg.dlg.data().updateData.pojo = data;
324 | let updated = $dlg.jsForm("get");
325 | $dlg.jsForm("fill", updated);
326 | };
327 | curDlg.dlg.dialog({
328 | autoOpen: false,
329 | width: curDlg.width || 400,
330 | modal: true,
331 | buttons: [
332 | {
333 | 'class': "ui-button-primary",
334 | text: i18n.dialog_ok,
335 | click: function () {
336 | saveDlg();
337 | curDlg.dlg.dialog("close");
338 | }
339 | },
340 | {
341 | text: i18n.dialog_cancel,
342 | click: function () {
343 | curDlg.dlg.dialog("close");
344 | }
345 | }
346 | ]
347 | });
348 | curDlg.dlg.jsForm();
349 | });
350 | }
351 | $dlg.jsForm();
352 | let $form = $dlg.find("form");
353 | if ($form) {
354 | $form.submit(function (ev) {
355 | ev.preventDefault();
356 | save(false);
357 | });
358 | }
359 | /**
360 | * prepare bootstrap tabs for js trigger
361 | * Fix for bootstrap bug
362 | */
363 | $dlg.find("a.nav-link").each(function (idx, ele) {
364 | let tabTrigger = new bootstrap.Tab(ele);
365 | $(ele).on('click', function (event) {
366 | event.preventDefault();
367 | tabTrigger.show();
368 | });
369 | });
370 | }
371 | Dialog.init = init;
372 | /**
373 | * Opens a dialog with the specified ID, populates it with data, and sets up event handlers.
374 | *
375 | * @param {string} id - The ID of the dialog element.
376 | * @param {object} data - The data to populate the dialog with.
377 | * @param {Function} done - The callback function to execute when the dialog is closed.
378 | * @param {Function} onData - The callback function to execute when new data is received.
379 | */
380 | function open(id, data, done, onData) {
381 | if (typeof Panel !== "undefined") {
382 | Panel.open(id, data, done, onData);
383 | return;
384 | }
385 | if (!data) {
386 | data = {};
387 | }
388 | // save the callback
389 | const $dlg = $(id);
390 | $dlg.data().onDone = done;
391 | $dlg.data().onData = onData;
392 | // toggle view/edit mode
393 | $dlg.jsForm("preventEditing", false);
394 | if (typeof data === "function") {
395 | data((retrieveData) => {
396 | $dlg.data().pojo = retrieveData;
397 | $dlg.jsForm("fill", retrieveData);
398 | if ($dlg.hasClass("view")) {
399 | $dlg.jsForm("preventEditing", true);
400 | }
401 | $dlg.dialog("open");
402 | });
403 | }
404 | else {
405 | if (!data) {
406 | data = {};
407 | }
408 | $dlg.data().pojo = data;
409 | $dlg.jsForm("fill", data);
410 | if ($dlg.hasClass("view")) {
411 | $dlg.jsForm("preventEditing", true);
412 | }
413 | $dlg.dialog("open");
414 | }
415 | }
416 | Dialog.open = open;
417 | /**
418 | * Closes the overlay or modal identified by the given ID.
419 | *
420 | * @param {string} id - The ID of the overlay or modal to be closed.
421 | */
422 | function close(id) {
423 | if (typeof Panel !== "undefined") {
424 | Panel.close(id);
425 | return;
426 | }
427 | const $dlg = $(id);
428 | $dlg.dialog("close");
429 | }
430 | Dialog.close = close;
431 | /**
432 | * Helper functions
433 | */
434 | /**
435 | * Replace default buttonpane if button div or/and button dropdown exists
436 | * @param dlg Dialog object contains HTMLDivElement
437 | */
438 | function replaceDefaultButtonPane(dlg) {
439 | if ($(dlg).find('.dialog-buttons').length > 0 || $(dlg).find('.dropdown').length > 0) {
440 | if ($(dlg).parent().find('.ui-dialog-buttonpane').length > 0) {
441 | $(dlg).parent().find('.ui-dialog-buttonpane').empty();
442 | if ($(dlg).find('.dialog-buttons').length === 1) {
443 | $(dlg).parent().find('.ui-dialog-buttonpane').append($(dlg).find('.dialog-buttons'));
444 | dispatchButtonEvents($(dlg).parent().find('.dialog-buttons'), dlg);
445 | }
446 | }
447 | else {
448 | $(dlg).parent().append('
');
449 | replaceDefaultButtonPane(dlg);
450 | }
451 | }
452 | }
453 | /**
454 | * Dispatch button events for the given button container and dialog.
455 | *
456 | * @param {HTMLElement} buttonContainer - The container element that holds the buttons.
457 | * @param {HTMLElement} dlg - The dialog element.
458 | */
459 | function dispatchButtonEvents(buttonContainer, dlg) {
460 | // Add click handler for event dispatching to every single element
461 | $(buttonContainer).children().each(function (index, element) {
462 | var _a;
463 | // For dropdown buttons set correct buttoncontainer
464 | if (element.nodeName === 'DIV') {
465 | dispatchButtonEvents($(dlg).parent().find('.dropdown-menu'), dlg);
466 | }
467 | // Check if button has data-event, dispatch event if element is not disabled
468 | if (element.hasAttribute('data-event') || ((_a = element === null || element === void 0 ? void 0 : element.children[0]) === null || _a === void 0 ? void 0 : _a.hasAttribute('data-event'))) {
469 | let el = element; // button or anchor
470 | if (!element.hasAttribute('data-event')) {
471 | el = element.children[0];
472 | }
473 | $(el).on('click', function () {
474 | if (this.classList.contains('disabled'))
475 | return;
476 | let data = null;
477 | if (this.classList.contains('data')) {
478 | data = $('.detail').jsForm('get');
479 | }
480 | //console.log('----- Dispatched event', $(this).data().event);
481 | dlg.trigger($(this).data().event, [dlg, data]);
482 | });
483 | }
484 | });
485 | }
486 | })(Dialog || (Dialog = {}));
487 |
--------------------------------------------------------------------------------
/controls/dialog.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 |
5 | /**
6 | * Documentation:(https://project.corinis.com/docs/pages/viewpage.action?pageId=99615166)
7 | *
8 | * Dialog Helper Class.
9 | * this works in combination with jqueryUI dialog and Connection Utils
10 | * to provide a generic CRUD dialog functionality
11 | *
12 | * This supports basic Dialogs and sub-dialogs.
13 | *
14 | * uses i18n to define button labels.
15 | * Use with following js:
16 | * ```js
17 | * subDialog = $("#subDialogId");
18 | * Dialog.init($detail, {
19 | * service: "InfoService",
20 | * saveMethod: "persist",
21 | * create: { action: "#addMenuBtn", data: {} }
22 | * update: { action: "#changeMenuBtn", data: ()=>{ return selectedItem; } }
23 | * view: { action: "#changeMenuBtn", data: ()=>{ return selectedItem; } }
24 | * subDialogs: [{
25 | * dlg: subDialog,
26 | * action: ".rawDetail"
27 | * }]
28 | * });
29 | * ```
30 | * Sample HTML:
31 | * ```
32 | *
41 | *
42 | *
46 | *
47 | * ```
48 | * Events
49 | * You can use the default dialog events to add extra actions:
50 | * ```
51 | * $dlg.on("dialogopen", function(){ alert("just opened a dialog"); });
52 | * $dlg.on("success", function(){ alert("just successfully saved"); });
53 | *
54 | * ```
55 | */
56 | namespace Dialog {
57 |
58 | class DialogConfig {
59 | service?: string;
60 | saveMethod?: string | ((data: any, any) => void);
61 | // have an extra save button not only ok (=save+close)
62 | buttonSave? : boolean;
63 | buttons? : Array | (($detail: JQuery, buttons: Array)=>any);
64 | create?: ActionConfig;
65 | update?: ActionConfig;
66 | view?: ActionConfig;
67 | /** show the menu floating (true) or in the header(false) */
68 | floatingMenu: false;
69 | onDone?:((data:any ) => boolean);
70 | icon?: string;
71 | color?: string;
72 | width?: number = 600;
73 | responsive?: number = 800;
74 | modal: boolean = true;
75 | subDialogs?: Array;
76 | overlay?: JQuery;
77 | }
78 |
79 | class ActionConfig {
80 | action: string;
81 | data: any;
82 | /**
83 | * before data is procesed
84 | */
85 | onData?:((data: any) => any);
86 | /**
87 | * after data is processed
88 | */
89 | onDone?:((data: any) => boolean);
90 | }
91 |
92 | class SubDialogConfig {
93 | action: string;
94 | dlg?: JQuery;
95 | width?: number = 400;
96 | }
97 |
98 | /**
99 | * Initializes the dialog overlay with the given ID and options.
100 | *
101 | * @param {any} id - the ID of the dialog
102 | * @param {object} options - optional settings for the dialog
103 | * @return {undefined}
104 | */
105 | export function init(id: string | JQuery, options?: DialogConfig, replaceButton:boolean = false) {
106 | if(typeof Panel !== "undefined") {
107 | console.log("Using panel init ", id, options);
108 | Panel.init(id, options);
109 | return;
110 | }
111 |
112 | if(replaceButton) {
113 | setTimeout(() => {
114 | console.log("Replace button pane...");
115 | replaceDefaultButtonPane($dlg);
116 | }, 100);
117 | }
118 |
119 | const $dlg = $(id);
120 |
121 | if(!$dlg.length) {
122 | console.log("Unable to find dialog: " + id, new Error().stack);
123 | return;
124 | }
125 |
126 | if(!options) {
127 | options = new DialogConfig ();
128 | }
129 |
130 | if($dlg.attr("data-modal") === "false") {
131 | options.modal = false;
132 | } else {
133 | options.modal = true;
134 | }
135 |
136 | if($dlg.attr("data-icon")) {
137 | options.icon = $dlg.attr("data-icon");
138 | }
139 | if($dlg.attr("data-color")) {
140 | options.color = $dlg.attr("data-color");
141 | }
142 | if($dlg.attr("data-width")) {
143 | options.width = Number($dlg.attr("data-width"));
144 | }
145 | if($dlg.attr("data-responsive")) {
146 | options.responsive = Number($dlg.attr("data-responsive"));
147 | }
148 | if($dlg.attr("data-buttonsave")) {
149 | options.buttonSave = $dlg.attr("data-buttonsave") === "true";
150 | }
151 |
152 | /**
153 | * Save the form (and close)
154 | */
155 | let save = function(close:boolean) {
156 | if(options?.saveMethod && !$dlg.hasClass("view")) {
157 | let data = $dlg.jsForm("get");
158 | if($dlg.data().onData) {
159 | data = $dlg.data().onData(data);
160 | }
161 | if(!data) {
162 | alert(i18n.dialog_validation_notOk);
163 | return;
164 | }
165 |
166 | if(typeof options.saveMethod === "function") {
167 | options.saveMethod(data, function(data){
168 | let postResult = null;
169 | if($dlg.data().onDone) {
170 | postResult = $dlg.data().onDone(data);
171 | }
172 | else if(options.onDone) {
173 | postResult = options.onDone(data);
174 | }
175 | $dlg.trigger("success", [data]);
176 | if(postResult === false || !close) {
177 | $dlg.jsForm("fill", data);
178 | return;
179 | }
180 | $dlg.dialog("close");
181 | });
182 | } else {
183 | Core.conn.execute(options.service, options.saveMethod, [data]).then(function(data){
184 | let postResult = null;
185 | if($dlg.data().onDone) {
186 | postResult = $dlg.data().onDone(data);
187 | }
188 | else if(options.onDone) {
189 | postResult = options.onDone(data);
190 | }
191 | $dlg.trigger("success", [data]);
192 |
193 | if(postResult === false || !close) {
194 | $dlg.jsForm("fill", data);
195 | return;
196 | }
197 | $dlg.dialog("close");
198 | });
199 | }
200 | } else if($dlg.data().onDone) {
201 | let data = $dlg.jsForm("get");
202 | // prepare data (if required)
203 | if($dlg.data().onData) {
204 | data = $dlg.data().onData(data);
205 | }
206 | // call the onDone function
207 | let res = $dlg.data().onDone(data, (data)=>{
208 | if(close || !data) {
209 | $dlg.dialog("close");
210 | } else {
211 | $dlg.jsForm("fill", data);
212 | }
213 | });
214 | if(res === false) {
215 | alert(i18n.dialog_validation_notOk);
216 | return;
217 | } else if(res === true) {
218 | // keep open, wait for callback
219 | } else {
220 | $dlg.dialog("close");
221 | }
222 |
223 | } else{
224 | $dlg.dialog("close");
225 | }
226 | };
227 |
228 | // action integration: open dialog
229 | if(options) {
230 | if(options.create) {
231 | $(options.create.action).click(()=>{
232 | if($(this).hasClass("disabled")) {
233 | return;
234 | }
235 | $dlg.removeClass("view");
236 | Dialog.open($dlg, options.create.data, options.create.onDone, options.create.onData);
237 | });
238 | }
239 | if(options.update) {
240 | $(options.update.action).click(()=>{
241 | if($(this).hasClass("disabled")) {
242 | return;
243 | }
244 | $dlg.removeClass("view");
245 | Dialog.open($dlg, options.update.data, options.update.onDone, options.update.onData);
246 | });
247 | }
248 | if(options.view) {
249 | $(options.view.action).click(()=>{
250 | if($(this).hasClass("disabled")) {
251 | return;
252 | }
253 | $dlg.addClass("view");
254 | Dialog.open($dlg, options.view.data, options.view.onDone, options.view.onData);
255 | });
256 | }
257 | }
258 |
259 | let buttons = []
260 |
261 |
262 | if(options.buttonSave) {
263 | buttons.push({
264 | 'class': "ui-button-primary",
265 | text: i18n.dialog_save,
266 | responsive: {
267 | html: ' ',
268 | position: 1
269 | },
270 | click: function() {
271 | save(false);
272 | }
273 | });
274 | }
275 |
276 | buttons.push({
277 | 'class': "ui-button-primary",
278 | text: i18n.dialog_ok,
279 | responsive: {
280 | html: ' ',
281 | position: 1
282 | },
283 | click: function() {
284 | save(true);
285 | }
286 | });
287 |
288 | buttons.push({
289 | text: i18n.dialog_cancel,
290 | click: function() {
291 | $dlg.dialog("close");
292 | }
293 | });
294 |
295 |
296 | // default buttons
297 | if(options.buttons) {
298 | if(typeof options.buttons === "function")
299 | buttons = options.buttons($dlg, buttons);
300 | else
301 | buttons = options.buttons;
302 | }
303 |
304 | // fill default class
305 | if(buttons?.length > 1) {
306 | buttons.forEach(item => {
307 | if(!item.class && item.color) {
308 | item.class = 'btn btn-' + item.color;
309 | }
310 | });
311 | }
312 |
313 |
314 | // check if overlay exists
315 | if(options.overlay && $(options.overlay).length === 0) {
316 | // remove overlay (not found)
317 | options.overlay = null;
318 | }
319 |
320 | $dlg.dialog({
321 | autoOpen: false,
322 | width: options.width || 600,
323 | modal: options.modal,
324 | titleIcon: {
325 | background: options.color,
326 | icon: options.icon
327 | },
328 | responsive: {
329 | overlay: options.overlay,
330 | limit: options.responsive || options.width || 600, // become responsive and full screen when screen width is <= 600 pixel
331 | left: {
332 | 'class': 'bg-icon active ' + options.color,
333 | text: ' ',
334 | click: function() {
335 | // reset changed fields
336 | if($(this).jsForm) {
337 | $(this).jsForm("resetChanged");
338 | }
339 | $(this).dialog("close");
340 | }
341 | },
342 | center: { 'class': 'has-icon' },
343 | right: true
344 | },
345 | buttons: buttons
346 |
347 | });
348 |
349 | if(options.subDialogs) {
350 | options.subDialogs.forEach((curDlg, index, array) => {
351 | $(curDlg.action, $dlg).click(function(){
352 | if($(this).hasClass("disabled")) {
353 | return;
354 | }
355 | curDlg.dlg.dialog("open");
356 | curDlg.dlg.data().updateData = $(this).closest(".POJO").data();
357 | curDlg.dlg.jsForm("fill", $(this).closest(".POJO").data().pojo);
358 | });
359 |
360 | let saveDlg = function() {
361 | let data = curDlg.dlg.jsForm("get");
362 | if(!data) {
363 | alert(i18n.dialog_validation_notOk);
364 | return;
365 | }
366 | // update original object
367 | curDlg.dlg.data().updateData.pojo = data;
368 | let updated = $dlg.jsForm("get");
369 | $dlg.jsForm("fill", updated);
370 | };
371 |
372 | curDlg.dlg.dialog({
373 | autoOpen: false,
374 | width: curDlg.width || 400,
375 | modal: true,
376 | buttons:
377 | [
378 | {
379 | 'class': "ui-button-primary",
380 | text: i18n.dialog_ok,
381 | click: function() {
382 | saveDlg();
383 | curDlg.dlg.dialog("close");
384 | }
385 | },
386 | {
387 | text: i18n.dialog_cancel,
388 | click: function() {
389 | curDlg.dlg.dialog("close");
390 | }
391 | }
392 | ]
393 | });
394 |
395 | curDlg.dlg.jsForm();
396 | });
397 | }
398 |
399 | $dlg.jsForm();
400 | let $form = $dlg.find("form");
401 | if($form) {
402 | $form.submit(function(ev){
403 | ev.preventDefault();
404 | save(false);
405 | });
406 | }
407 |
408 | /**
409 | * prepare bootstrap tabs for js trigger
410 | * Fix for bootstrap bug
411 | */
412 | $dlg.find("a.nav-link").each(function (idx, ele) {
413 | let tabTrigger = new bootstrap.Tab(ele);
414 | $(ele).on('click', function (event) {
415 | event.preventDefault();
416 | tabTrigger.show();
417 | });
418 | });
419 |
420 | }
421 |
422 | /**
423 | * Opens a dialog with the specified ID, populates it with data, and sets up event handlers.
424 | *
425 | * @param {string} id - The ID of the dialog element.
426 | * @param {object} data - The data to populate the dialog with.
427 | * @param {Function} done - The callback function to execute when the dialog is closed.
428 | * @param {Function} onData - The callback function to execute when new data is received.
429 | */
430 | export function open(id: string | JQuery, data: any, done?: ((data: any, cb: ((data: any)=>void)) => boolean), onData?: ((data: any) => any)) {
431 | if(typeof Panel !== "undefined") {
432 | Panel.open(id, data, done, onData);
433 | return;
434 | }
435 |
436 | if (!data) {
437 | data = {};
438 | }
439 |
440 | // save the callback
441 | const $dlg = $(id);
442 | $dlg.data().onDone = done;
443 | $dlg.data().onData = onData;
444 |
445 | // toggle view/edit mode
446 | $dlg.jsForm("preventEditing", false);
447 |
448 | if(typeof data === "function") {
449 | data((retrieveData: any)=>{
450 | $dlg.data().pojo = retrieveData;
451 | $dlg.jsForm("fill", retrieveData);
452 | if($dlg.hasClass("view")) {
453 | $dlg.jsForm("preventEditing", true);
454 | }
455 | $dlg.dialog("open");
456 | });
457 | } else {
458 | if(!data) {
459 | data = {};
460 | }
461 |
462 | $dlg.data().pojo = data;
463 | $dlg.jsForm("fill", data);
464 | if($dlg.hasClass("view")) {
465 | $dlg.jsForm("preventEditing", true);
466 | }
467 | $dlg.dialog("open");
468 | }
469 | }
470 |
471 | /**
472 | * Closes the overlay or modal identified by the given ID.
473 | *
474 | * @param {string} id - The ID of the overlay or modal to be closed.
475 | */
476 | export function close(id: string | JQuery): void {
477 | if(typeof Panel !== "undefined") {
478 | Panel.close(id);
479 | return;
480 | }
481 | const $dlg = $(id);
482 | $dlg.dialog("close");
483 | }
484 |
485 |
486 | /**
487 | * Helper functions
488 | */
489 |
490 |
491 | /**
492 | * Replace default buttonpane if button div or/and button dropdown exists
493 | * @param dlg Dialog object contains HTMLDivElement
494 | */
495 | function replaceDefaultButtonPane(dlg: object) {
496 | if ($(dlg).find('.dialog-buttons').length > 0 || $(dlg).find('.dropdown').length > 0) {
497 | if ($(dlg).parent().find('.ui-dialog-buttonpane').length > 0) {
498 | $(dlg).parent().find('.ui-dialog-buttonpane').empty();
499 |
500 | if ($(dlg).find('.dialog-buttons').length === 1) {
501 | $(dlg).parent().find('.ui-dialog-buttonpane').append($(dlg).find('.dialog-buttons'));
502 | dispatchButtonEvents($(dlg).parent().find('.dialog-buttons') , dlg);
503 | }
504 | }
505 | else {
506 | $(dlg).parent().append('
');
507 | replaceDefaultButtonPane(dlg);
508 | }
509 | }
510 | }
511 |
512 | /**
513 | * Dispatch button events for the given button container and dialog.
514 | *
515 | * @param {HTMLElement} buttonContainer - The container element that holds the buttons.
516 | * @param {HTMLElement} dlg - The dialog element.
517 | */
518 | function dispatchButtonEvents(buttonContainer: JQuery, dlg: JQuery): void {
519 | // Add click handler for event dispatching to every single element
520 | $(buttonContainer).children().each(function(index, element) {
521 |
522 | // For dropdown buttons set correct buttoncontainer
523 | if(element.nodeName === 'DIV') {
524 | dispatchButtonEvents($(dlg).parent().find('.dropdown-menu'), dlg);
525 | }
526 |
527 | // Check if button has data-event, dispatch event if element is not disabled
528 | if (element.hasAttribute('data-event') || element?.children[0]?.hasAttribute('data-event')) {
529 | let el: any = element; // button or anchor
530 | if (!element.hasAttribute('data-event')) {
531 | el= element.children[0];
532 | }
533 |
534 | $(el).on('click', function() {
535 | if (this.classList.contains('disabled'))
536 | return;
537 |
538 | let data = null;
539 | if (this.classList.contains('data')) {
540 | data = $('.detail').jsForm('get');
541 | }
542 | //console.log('----- Dispatched event', $(this).data().event);
543 | dlg.trigger($(this).data().event, [dlg, data] );
544 | });
545 | }
546 | });
547 | }
548 |
549 | }
--------------------------------------------------------------------------------
/controls/jquery.jsform.bsfileUpload.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 |
3 | let uploadModal = new bootstrap.Modal(document.getElementById('uploadDlg'), {});
4 |
5 | $(".jsfileupload").on("click", function(){
6 | let options = {
7 | title: $(this).data().title || "Upload",
8 | service: $(this).data().service,
9 | method: $(this).data().method || "method",
10 | params: $(this).data().params || [],
11 | ele: $(this)
12 | }
13 |
14 | $("#uploadDlg .modal-title").html(options.title);
15 | $("#uploadDlgLoadFile").data().options = options;
16 |
17 | // reset
18 | $("#uploadDlgStatus").hide();
19 | $("#uploadDlgProgress").hide();
20 | $("#uploadDlgBtn").show();
21 |
22 | uploadModal.show();
23 | });
24 |
25 | $("#uploadDlgLoadFile").on("change", function(){
26 | // read data
27 | $("#uploadDlgProgress").show();
28 | $("#uploadDlgBtn").hide();
29 |
30 | let options = $("#uploadDlgLoadFile").data().options;
31 |
32 | let done = 0;
33 |
34 | let allFiles = $('#uploadDlgLoadFile')[0].files
35 |
36 | let result = [];
37 |
38 | console.log("files", allFiles);
39 | for(let i = 0; i < allFiles.length; i++)
40 | {
41 | let files = allFiles[i];
42 | let fd = new FormData();
43 | let transData = {
44 | service: options.service,
45 | method: options.method,
46 | param: options.params,
47 | };
48 | fd.append('data', JSON.stringify(transData));
49 | fd.append('files[]',files);
50 |
51 | // upload
52 | $.ajax({
53 | url: Core.conn.SERVICE_URL ? Core.conn.SERVICE_URL : '/service',
54 | type: 'post',
55 | data: fd,
56 | contentType: false,
57 | processData: false
58 | }).done(function(data, textStatus, _xhr) {
59 | done++;
60 | data = data.length > 0 ? data[0] : data;
61 |
62 | if(data.success === false || data.error) {
63 | $("#uploadDlgStatus").html("Error: " + data.longMessage);
64 | options.ele.trigger("error", [data.error]);
65 | return;
66 | }
67 |
68 | result.push(data.data);
69 |
70 | if(done == $('#uploadDlgLoadFile')[0].files.length) {
71 | options.ele.trigger("success", [result]);
72 | uploadModal.hide();
73 | }
74 | }).fail(function(_xhr, textStatus, error) {
75 | done++;
76 | console.log("error", textStatus, error);
77 | $("#uploadDlgStatus").html("Error: " + textStatus);
78 | options.ele.trigger("error", [error]);
79 | });
80 | }
81 | });
82 |
83 | });
--------------------------------------------------------------------------------
/controls/jquery.responsiveDialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Initialize a dialog for a detail window
3 | *
4 | * this is basically just a a wrapper for the jquery ui dialog.
5 | * an additional feature is, that on small screens (i.e. mobile)
6 | * it will instantiate a sliding panel (from the left) filling the whole screen.
7 | * - close will move to the left and turn into a back arrow
8 | * - other buttons will move to the top right (i.e. save) but with an icon
9 | *
10 | * You can force responsive dialog by setting $(document).data().mobile to true
11 | * # Usage
12 | *
13 | * Init the dialog as normal and add responsive configuration options.
14 | * Note: this works only when creating the dialog, not when setting the
15 | * options later:
16 | *
17 | *
18 | $detail.dialog({
19 | width: 600,
20 | height: 450,
21 | overlay: $("#main"),
22 | savePosition: "uniqueId",
23 | responsive: {
24 | limit: 600,
25 | left: {
26 | 'class': 'red-style',
27 | text: ' Icon',
28 | click: function() {
29 | $(this).dialog("close");
30 | }
31 | },
32 | center: { 'class': 'green-background' },
33 | right: true
34 | },
35 | closeOnEscape: true,
36 | buttons: [
37 | {
38 | text: i18n.dialog_ok,
39 | responsive: {
40 | html: ' ',
41 | position: 1
42 | },
43 | click: function() {
44 | alert("OK");
45 | }
46 | },
47 | {
48 | text: i18n.dialog_cancel,
49 | responsive: false,
50 | click: function() {
51 | $(this).dialog("close");
52 | }
53 | }
54 | ]
55 | };
56 | */
57 | $.widget("ui.dialog", $.ui.dialog, {
58 | windowResize: false,
59 | fullScreen: null,
60 | overlayTitlebar: false,
61 | resizeto: null,
62 |
63 | open: function() {
64 |
65 | var that = this;
66 |
67 | // a responsive dialog
68 | if(this.options.responsive) {
69 | var isResponsive = ($(window).width() <= this.options.responsive.limit) || $(document).data().mobile || this.options.overlay;
70 |
71 | if(this.options.responsive.overlay && !isResponsive) {
72 | this.uiDialogTitlebar.show();
73 | this.uiDialogTitlepane.hide();
74 | this.uiDialog.removeClass("ui-corner-all").addClass("resp-fullscreen");
75 | this._setOption("draggable", false);
76 |
77 | if($(this.options.responsive.overlay).length > 0) {
78 | this.resizeto = $(this.options.responsive.overlay);
79 | }
80 |
81 | // create the right title bar
82 | if(!this.overlayTitlebar) {
83 | // move the buttons in the header instead of the "x"
84 | var buttonPane = this.uiDialogButtonPane.detach();
85 | buttonPane.find(".ui-dialog-buttonset").addClass("btn-group");
86 | buttonPane.css({
87 | position: "absolute",
88 | top: 8,
89 | right: 5
90 | });
91 | this.uiDialogTitlebar.css({
92 | background: "#f5f5f5"
93 | });
94 |
95 | buttonPane.removeClass("ui-dialog-buttonpane").show();
96 | $("button", this.uiDialogTitlebar).hide();
97 | this.uiDialogTitlebar.append(buttonPane);
98 | this.overlayTitlebar = true;
99 | }
100 |
101 |
102 | // set full screen flag
103 | this.fullScreen = "overlay";
104 |
105 | if(!this.windowResize) {
106 | this.windowResize = true;
107 | $(window).resize(function() {
108 | that._position();
109 | });
110 | }
111 |
112 | } else {
113 |
114 | // create the right title bar
115 | if(this.overlayTitlebar) {
116 | // move the buttons in the header instead of the "x"
117 | var buttonPane = this.uiDialogButtonPane.detach();
118 | buttonPane.css({
119 | position: "block",
120 | top: "auto",
121 | right: "auto"
122 | });
123 | this.uiDialogTitlebar.css({
124 | background: "white"
125 | });
126 |
127 | buttonPane.addClass("ui-dialog-buttonpane");
128 | $("button", this.uiDialogTitlebar).show();
129 | this.element.append(buttonPane);
130 | this.overlayTitlebar = false;
131 | }
132 |
133 |
134 | // reset changes if it WAS fullscreen
135 | if(this.fullScreen && !isResponsive) {
136 | this.uiDialogTitlebar.show();
137 | this.uiDialogButtonPane.show();
138 | this.uiDialogTitlepane.hide();
139 | $(".titleIcon", this.uiDialogTitlebar).show();
140 | this._setOption("draggable", true);
141 | this.uiDialog.addClass("ui-corner-all").removeClass("resp-fullscreen");
142 | this.uiDialog.css("margin", "auto");
143 | }
144 |
145 | if(isResponsive) {
146 | // set full screen flag
147 | this.fullScreen = "full";
148 |
149 | // hide the default panes
150 | $(".titleIcon", this.uiDialogTitlepane).hide();
151 | this.uiDialogTitlebar.hide();
152 | this.uiDialogButtonPane.hide();
153 | this.uiDialogTitlepane.show();
154 | this.uiDialog.removeClass("ui-corner-all").addClass("resp-fullscreen");;
155 | this._setOption("draggable", false);
156 | this.uiDialog.css("margin", "-2px"); // remove extra border
157 | }
158 | }
159 |
160 | }
161 |
162 | // auto save position and size
163 | if(this.options.savePosition && !this.fullScreen) {
164 | var saveKey = this.options.savePosition;
165 | // set the default position and w/h
166 | var defaultPos = localStorage ? localStorage.getItem(saveKey) : null;
167 | if(!defaultPos) {
168 | defaultPos = {
169 | width: this.options.width,
170 | height: this.options.height,
171 | position: this.options.position
172 | }
173 | } else {
174 | defaultPos = JSON.parse(defaultPos);
175 | }
176 |
177 |
178 | if(defaultPos.position) {
179 | if(!defaultPos.position.of)
180 | defaultPos.position.of = window;
181 | } else {
182 | defaultPos.position = {
183 | my: "center", at: "center", of: window
184 | };
185 | }
186 |
187 | this.options.width = defaultPos.width;
188 | this.options.height = defaultPos.height;
189 | this.options.position = defaultPos.position;
190 |
191 | // add drag and resize handler
192 | var savePos = function(event, ui) {
193 | if(this.fullScreen) {
194 | return;
195 | }
196 | var pos = {
197 | width: $(this).dialog("option", "width"),
198 | height: $(this).dialog("option", "height"),
199 | position: {
200 | my: "left top",
201 | at: "left+" + ui.position.left + " top+" + ui.position.top
202 | }
203 | }
204 |
205 | localStorage.setItem(saveKey, JSON.stringify(pos));
206 | };
207 |
208 | this.options.dragStop = savePos;
209 | this.options.resizeStop = savePos;
210 | }
211 |
212 |
213 | // invoke the parent widget
214 | var wasOpen = this._isOpen;
215 | var resp = this._super();
216 | if(wasOpen) {
217 | this._trigger( "open" );
218 | }
219 |
220 | return resp;
221 | },
222 |
223 | _size: function() {
224 | if(this.fullScreen === "overlay") {
225 | // Reset content sizing
226 | this.element.show().css( {
227 | width: "auto",
228 | minHeight: 0,
229 | maxHeight: "none",
230 | height: 0
231 | } );
232 |
233 | this.element.css({
234 | height: "100%",
235 | width: "100%",
236 | position: "absolute"
237 | });
238 | // check the size of what we want to overlay
239 | this.uiDialog.show().css({
240 | width: "100%",
241 | height: "100%"
242 | });
243 | }
244 | else if(this.fullScreen === "full") {
245 |
246 | // Reset content sizing
247 | this.element.show().css( {
248 | width: "auto",
249 | minHeight: 0,
250 | maxHeight: "none",
251 | height: 0
252 | } );
253 |
254 | var nonContentHeight = this.uiDialog.css( {
255 | height: "auto",
256 | width: "100%",
257 | } ).outerHeight();
258 |
259 |
260 | this.element.css({
261 | height: "100%",
262 | width: "100%",
263 | position: "absolute",
264 | top: "48px"
265 | });
266 |
267 | // full screen
268 | this.uiDialog.show().css({
269 | width: "100%",
270 | height: "100%"
271 | });
272 | } else {
273 | this._super();
274 | }
275 | },
276 |
277 | _position: function() {
278 | if(this.fullScreen === "overlay") {
279 | // calculate the new position
280 | // two possibilities: either left - or based on width
281 | if(this.options.responsive.overlay.position) {
282 | this.uiDialog.css({
283 | left: this.options.responsive.overlay.position().left,
284 | height: $(window).height() - this.options.responsive.overlay.position().top,
285 | width: $(window).width() - this.options.responsive.overlay.position().left,
286 | top: this.options.responsive.overlay.position().top
287 | });
288 | }
289 | else if(this.options.responsive.overlay.left) {
290 | this.uiDialog.css({
291 | left: this.options.responsive.overlay.left,
292 | height: $(window).height() - this.options.responsive.overlay.top,
293 | width: $(window).width() - this.options.responsive.overlay.left,
294 | top: this.options.responsive.overlay.top
295 | });
296 | } else {
297 | this.uiDialog.css({
298 | left: $(window).width() - this.options.width,
299 | height: $(window).height() - this.options.responsive.overlay.top,
300 | width: this.options.width,
301 | top: this.options.responsive.overlay.top
302 | });
303 | }
304 | }
305 | else if(this.fullScreen === "full") {
306 | this.uiDialog.position({my: "left top", at: "left top", of: window})
307 | } else {
308 | this._super();
309 |
310 | // shrink to avoid overwriting the window size (async to allow painting of content)
311 | var $ele = this.element;
312 | setTimeout(function(){
313 | if($ele.height() > $(window).height() - 150) {
314 | $ele.height($(window).height() - 130);
315 | }
316 | }, 30);
317 | }
318 | },
319 |
320 | _createButtons: function() {
321 | var that = this,
322 | buttons = this.options.buttons;
323 |
324 | // now go over over buttons and see if any have a pulldown
325 | $.each( buttons, function( name, props ) {
326 | if(!props["class"])
327 | props["class"] = "btn btn-secondary";
328 | else if(props["class"].indexOf("ui-button-primary") !== -1) {
329 | props["class"] = props["class"] + " btn btn-primary";
330 | } else
331 | props["class"] = props["class"] + " btn btn-secondary";
332 |
333 | if(!props.pulldown) {
334 | return;
335 | }
336 | var cur = props;
337 | if(!cur.id) {
338 | cur.id = "pd-" + new Date().getTime() - Math.random() * 10000;
339 | }
340 |
341 | // add the pulldown
342 | props.click = function() {
343 | if(that._pulldown) {
344 | that._pulldown.item.remove();
345 | delete(that._pulldown);
346 | // we clicked on the same
347 | if(that._pulldown.id == cur.id)
348 | return;
349 | }
350 |
351 | // create the html
352 | var container = $("
")
353 | var pd = $("");
354 | pd.css("min-width", $("#" + cur.id).outerWidth());
355 | container.append(pd);
356 | $.each(cur.pulldown.items, function(){
357 | var entry = $(" ");
358 | entry.append("" + this.html + "
");
359 | entry.click(this.click);
360 | entry.on("close", function(){
361 | that._pulldown.item.remove();
362 | delete that._pulldown;
363 | });
364 | pd.append(entry)
365 | });
366 | $(that.uiDialog).append(container);
367 | that._pulldown = { item: container, id: cur.id};
368 | pd.menu({
369 | select: function(event, ui) {
370 | $(ui.item).click();
371 | }
372 | });
373 | container.position({
374 | my: "left top",
375 | at: "left bottom",
376 | of: "#" + cur.id,
377 | });
378 |
379 | // call the open
380 | if(cur.pulldown.open) {
381 | $.proxy(cur.pulldown.open, pd);
382 | }
383 | }
384 | });
385 |
386 |
387 | this._createPaneButtons();
388 | this._super();
389 | },
390 |
391 | _createPaneButtons: function() {
392 | if(!this.options.responsive || !this.uiDialogTitlepaneRight) {
393 | return;
394 | }
395 | var that = this,
396 | buttons = this.options.buttons,
397 | config = this.options.responsive;
398 | if(!config) {
399 | return;
400 | }
401 |
402 | // remove existing buttons
403 | this.uiDialogTitlepaneRight.empty();
404 | var buttonArr =[];
405 | $.each( buttons, function( name, props ) {
406 | if(!props.responsive) {
407 | return;
408 | }
409 |
410 |
411 | var click, buttonOptions;
412 |
413 | props = $.isFunction( props ) ?
414 | { click: props, text: name } :
415 | props;
416 |
417 | // Default to a non-submitting button
418 | props = $.extend( { type: "button" }, props );
419 |
420 | // Change the context for the click callback to be the main element
421 | click = props.click;
422 | buttonOptions = {
423 | icon: props.responsive.icon || props.icon,
424 | iconPosition: props.iconPosition,
425 | pos: props.responsive.position || 1,
426 | html: props.responsive.html
427 | };
428 |
429 | delete props.click;
430 | delete props.responsive;
431 | delete props.icon;
432 | delete props.iconPosition;
433 | delete props.showLabel;
434 |
435 | // sort
436 | buttonArr.push({props: props, options: buttonOptions, click:click})
437 | } );
438 |
439 | buttonArr.sort(function(a,b){
440 | return a.options.pos - b.options.pos
441 | });
442 |
443 | $.each(buttonArr, function(){
444 | var button = this;
445 | if(button.options.html) {
446 | $(button.options.html).appendTo( that.uiDialogTitlepaneRight )
447 | .on( "click", function() {
448 | button.click.apply( that.element[ 0 ], arguments );
449 | } );
450 | } else {
451 | $( " ", button.props )
452 | .btn( button.options )
453 | .appendTo( that.uiDialogTitlepaneRight )
454 | .on( "click", function() {
455 | button.click.apply( that.element[ 0 ], arguments );
456 | } );
457 | }
458 | });
459 | },
460 |
461 | /**
462 | * create a combined title bar/button pane for responsive layout
463 | */
464 | _createTitlepane: function() {
465 | var that = this;
466 | var config = this.options.responsive;
467 | if(!config) {
468 | return;
469 | }
470 |
471 | this.uiDialogTitlepane = $( '' );
472 | this.uiDialogTitlepane.hide();
473 | this.uiDialogTitlepane.addClass("ui-dialog-titlebar ui-widget-header ui-helper-clearfix ui-dialog-titlepane resp-fullscreen" );
474 |
475 |
476 | // four parts: left-buttons - title - rightbuttons - menu
477 | this.uiDialogTitlepaneLeft = $( "
" );
478 | if(config.left) {
479 | if(config.left['class']) {
480 | this.uiDialogTitlepaneLeft.addClass(config.left['class']);
481 | }
482 | if(config.left.text) {
483 | this.uiDialogTitlepaneLeft.html(config.left.text);
484 | } else {
485 | this.uiDialogTitlepaneLeft.html(" ");
486 | }
487 | if(config.left.click) {
488 | this.uiDialogTitlepaneLeft.on("click", function(){
489 | config.left.click.apply( that.element[ 0 ], arguments );
490 | });
491 | }
492 | }
493 |
494 | this.uiDialogTitlepaneRight = $( "
" );
495 | if(config.right) {
496 | if(config.right['class']) {
497 | this.uiDialogTitlepaneRight.addClass(config.right['class']);
498 | }
499 | // content function (use append)
500 | if(config.right.content) {
501 | config.right.content(this.uiDialogTitlepaneRight);
502 | } else {
503 | // create based on the buttons
504 | this._createPaneButtons();
505 | }
506 | }
507 |
508 | this.uiDialogTitlepaneTitle = $( "
" );
509 | if(config.center) {
510 | if(config.center['class']) {
511 | this.uiDialogTitlepaneTitle.addClass(config.center['class']);
512 | }
513 | if(config.center.text) {
514 | this.uiDialogTitlepaneTitle.html(config.center.text);
515 | } else {
516 | this._title(this.uiDialogTitlepaneTitle);
517 | }
518 | if(config.center.click) {
519 | this.uiDialogTitlepaneTitle.on("click", function(){
520 | config.center.click.apply( that.element[ 0 ], arguments );
521 | });
522 | }
523 | } else {
524 | this._title(this.uiDialogTitlepaneTitle, config.title);
525 | }
526 |
527 | this.uiDialogTitlepaneLeft.css({
528 | float:"left"
529 | });
530 |
531 | this.uiDialogTitlepane.append(this.uiDialogTitlepaneLeft);
532 |
533 | this.uiDialogTitlepane.append(this.uiDialogTitlepaneTitle);
534 |
535 | // has to be within title pane to allow click events
536 | this.uiDialogTitlepane.append(this.uiDialogTitlepaneRight);
537 |
538 | // add on top
539 | this.uiDialogTitlepane.prependTo(this.uiDialog);
540 | },
541 |
542 | _title: function(title, xtra) {
543 | this._super(title);
544 | var that = this;
545 | // add icon
546 | var opts = this.options.titleIcon;
547 | if(opts || xtra) {
548 | title.addClass("has-icon");
549 | var bg = opts?opts.background:'';
550 | if(!bg) {
551 | bg = '';
552 | }
553 |
554 | title.html(
555 | ' ' +
556 | (xtra?' ':'') +
557 | ' ' +
558 | ' '
559 | + title.html());
560 |
561 | if(xtra && xtra.click) {
562 | title.addClass("xtra");
563 | title.children("span").click(function(){
564 | xtra.click.apply( that.element[ 0 ], arguments );
565 | });
566 | }
567 | }
568 | },
569 |
570 | _create: function() {
571 | this._super();
572 | this._createTitlepane();
573 | },
574 |
575 | _init: function() {
576 |
577 | // check/fix the options
578 | if(this.options.responsive) {
579 | if(this.options.responsive === true) {
580 | this.options.responsive = {};
581 | }
582 | var responsiveOpts = $.extend({}, {
583 | /**
584 | * the limit when the responsive full screen dialog should appear
585 | */
586 | limit: 1000,
587 | /**
588 | * which effect (can be just the name or the options) to use when showing/hiding the screen
589 | */
590 | effect: { effect: "slide" }
591 | }, this.options.responsive);
592 | this.options.responsive = responsiveOpts;
593 |
594 | if(this.options.responsive.overlay && !this.options.responsive.overlay.top) {
595 | this.options.responsive.overlay.top = 121;
596 | }
597 | }
598 |
599 | return this._super();
600 | },
601 |
602 | close: function() {
603 | return this._super();
604 | }
605 | });
606 | /**
607 | * btn override:
608 | * type: [outline-][primary, *secondary*, success, danger, warning, info, light, dark]
609 | */
610 | $.widget("corinis.btn", $.ui.button, {
611 | options: {
612 | type: "secondary"
613 | },
614 | _create: function() {
615 | this._super();
616 | var type = this.options.type || "secondary";
617 | if($(this).hasClass("ui-button-primary"))
618 | type = "primary";
619 | this._addClass("btn btn-" + type);
620 | this._removeClass("ui-corner-all ui-widget ui-button");
621 | }
622 | });
623 |
--------------------------------------------------------------------------------
/controls/jquery.sorTable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jquery.sorTable
3 | * -----------
4 | * Add some basic functions like sorting and selecting of rows to an existing table.
5 | *
6 | * For this to work, there needs to be a header row that controls the sorting. This is either:
7 | * - "table>thead>tr>th","table>thead>tr>td" or "table>tr[eq:0]>td"
8 | * can be specified in the "headSelect" option
9 | * the rows sorted are either "table>tbody>tr", "table>tr" or "table>tr[pos()>0]" depending on the html layout (i.e. with a thead).
10 | * the can be specified in the "bodyRowSelect" option. the cells are ALWAYS td for the body!
11 | *
12 | * Every time you call refresh the body will be selected new, so you can use this even with dynamically added tables.
13 | *
14 | * Note that the header cells must be matched by body cells in order for sorting to work. If there is a different amount of body cells
15 | * there will be unintended effects.
16 | *
17 | * @version 1.0
18 | * @class
19 | * @author Niko Berger
20 | * @license MIT License GPL
21 | */
22 | ;(function( $, window, undefined ){
23 | "use strict";
24 |
25 | const SORTABLE_INIT_FUNCTIONS = {}; // remember initialization functions
26 | const SORTABLE_MAP = {}; // remember all sorTables
27 |
28 | /**
29 | * @param element {Node} the cotnainer table that should be converted to a sorTable
30 | * @param options {object} the configuraton object
31 | * @constructor
32 | */
33 | function SorTable (element, options) {
34 | // create the options
35 | this.options = $.extend({}, {
36 | /**
37 | * add icons in the header: add html snipped.
38 | * If empty, nothing will be shown
39 | */
40 | icon: [' ', ' ', ' '],
41 | /**
42 | * selector to get the header cells
43 | */
44 | headSelect: "thead>tr>th",
45 | /**
46 | * select the body
47 | */
48 | bodySelect: "tbody",
49 | /**
50 | * the class in the head that specifies a row that is sortable
51 | */
52 | classSortable: "sortable",
53 | /**
54 | * quickfilter element
55 | */
56 | quickfilter: null,
57 | /**
58 | * remember the sort order in a cookie (non-null = name of the cookie)
59 | */
60 | remember: null
61 | }, options);
62 | // the table to sort
63 | this.element = element;
64 | // the sorted last
65 | this.lastSort = null;
66 |
67 | this._init();
68 | }
69 |
70 | /**
71 | * init and load the config
72 | * @private
73 | */
74 | SorTable.prototype._init = function() {
75 | const that = this;
76 | $(this.options.headSelect, this.element).each(function(){
77 | if(!$(this).hasClass(that.options.classSortable))
78 | return;
79 | let sortIcon = null;
80 | if(that.options.icon) {
81 | sortIcon = $(''+that.options.icon[0]+'');
82 | $(this).append(sortIcon);
83 | }
84 |
85 | $(this).click(function(){
86 | const curSort = that._sort($(this).prevAll().length, that.lastSort !== sortIcon ? 0 : $(this).data().sortOrder);
87 | $(this).data().sortOrder = curSort;
88 | if(sortIcon) {
89 | if(that.lastSort) that.lastSort.html(that.options.icon[0]);
90 | // remember the current row and set the icon correctly
91 | that.lastSort = sortIcon;
92 | sortIcon.html(that.options.icon[curSort]);
93 | }
94 | });
95 | });
96 |
97 | const qf = $(this.options.headSelect, this.element).find("input.quickfilter");
98 | if(qf.length > 0) {
99 | qf.on("keyup change", function(){
100 | const val = $(this).val().trim();
101 | that._filter(val);
102 | });
103 | }
104 |
105 | };
106 |
107 |
108 | /**
109 | * filter the body table based on a value
110 | */
111 | SorTable.prototype._filter = function(value) {
112 | const that = this;
113 | const $body = $(this.options.bodySelect, this.element);
114 | value = value.toLowerCase();
115 | $($body).children("tr").each(function(){
116 | if(!value || value === "") {
117 | $(this).show();
118 | return;
119 | }
120 | // check the text content
121 | const content = that._rowVal($(this)).toLowerCase();
122 | if(content.indexOf(value) === -1)
123 | $(this).hide();
124 | else
125 | $(this).show();
126 | });
127 | };
128 |
129 | /**
130 | * get the content of a cell
131 | */
132 | SorTable.prototype._cellVal= function($cell) {
133 |
134 | let rowVal = $("select", $cell).val();
135 | // no text - check input
136 | if(typeof rowVal === "undefined") {
137 | rowVal = $("input", $cell).val();
138 | }
139 | if(typeof rowVal === "undefined") {
140 | rowVal = $cell.text().trim();
141 | }
142 | return rowVal;
143 | }
144 |
145 | /**
146 | * get the content of a row
147 | */
148 | SorTable.prototype._rowVal= function($row) {
149 | const that = this;
150 | let rowVal = "";
151 | $row.children().each(function(){
152 | rowVal += that._cellVal($(this));
153 | });
154 | return rowVal;
155 | }
156 |
157 | /**
158 | * repaint the sorTable with new data
159 | * @param pos the row to sort
160 | * @param order the current order of this row
161 | * @return 0-no sort; 1-down; 2-up
162 | * @private
163 | */
164 | SorTable.prototype._sort = function(pos, order) {
165 | const that = this;
166 | const $body = $(this.options.bodySelect, this.element);
167 | let dataType = "string";
168 | if(order == 1)
169 | order = 2;
170 | else
171 | order = 1;
172 |
173 | const he = $($(this.options.headSelect, this.element).get(pos));
174 | if(he.hasClass("num") || he.hasClass("number"))
175 | dataType = "number";
176 | else if(he.hasClass("trimnum"))
177 | dataType = "findnumber";
178 | else if(he.hasClass("date"))
179 | dataType = "date";
180 |
181 | console.log("sort "+pos+" by", dataType);
182 |
183 | // collect all tr
184 | const data = [];
185 | $($body).children("tr").each(function(){
186 | const $cell = $($(this).children()[pos]);
187 | const rowVal = that._cellVal($cell);
188 | data.push({
189 | val: rowVal,
190 | row: $(this).detach()
191 | });
192 | });
193 |
194 | let valFunc = he.data().sorter;
195 | if(!valFunc)
196 | switch(dataType){
197 | case "number":
198 | valFunc = (a)=>{
199 | return Number(a.val);
200 | }; break;
201 | case "findnumber":
202 | valFunc = (a)=>{
203 | const aval = a.val.replace(/[^0-9.,]+/g, "");
204 | return Number(aval);
205 | }; break;
206 | case "date":
207 | valFunc = (a)=>{
208 | const aval = new Date(a);
209 | return aval.getTime();
210 | }; break;
211 | default:
212 | valFunc = (a)=>{
213 | return a.val;
214 | }; break;
215 | }
216 |
217 | // sort
218 | data.sort(function(a,b){
219 | const aVal = valFunc(a);
220 | const bVal = valFunc(b);
221 | if (aVal < bVal)
222 | return order == 1 ? -1 : 1;
223 | if (aVal > bVal)
224 | return order == 1 ? 1 : -1;
225 | return 0;
226 | });
227 |
228 | // readd
229 | $.each(data, function(){
230 | $body.append(this.row);
231 | });
232 |
233 | return order;
234 | };
235 |
236 | SorTable.prototype.sort = function (col) {
237 | if(!isNaN(col)) {
238 | this._sort(col);
239 | return;
240 | }
241 | this._sort(col.prevAll().length);
242 | };
243 |
244 | /**
245 | * destroy the jsform and its resources.
246 | * @private
247 | */
248 | SorTable.prototype.destroy = function( ) {
249 | return $(this.element).each(function(){
250 | $(window).unbind('.sorTable');
251 | $(this).removeData('sorTable');
252 | });
253 | };
254 |
255 | // init and call methods
256 | $.fn.sorTable = function ( method ) {
257 | // Method calling logic
258 | if ( typeof method === 'object' || ! method ) {
259 | return this.each(function () {
260 | if (!$(this).data('sorTable')) {
261 | $(this).data('sorTable', new SorTable( this, method ));
262 | }
263 | });
264 | } else {
265 | const args = Array.prototype.slice.call( arguments, 1 );
266 | let sorTable;
267 | // none found
268 | if(this.length === 0) {
269 | return null;
270 | }
271 | // only one - return directly
272 | if(this.length === 1) {
273 | sorTable = $(this).data('sorTable');
274 | if (sorTable) {
275 | if(method.indexOf("_") !== 0 && sorTable[method]) {
276 | return sorTable[method].apply(sorTable, args);
277 | }
278 |
279 | $.error( 'Method ' + method + ' does not exist on jQuery.sorTable' );
280 | return false;
281 | }
282 | }
283 |
284 | return this.each(function () {
285 | sorTable = $.data(this, 'sorTable');
286 | if (sorTable) {
287 | if(method.indexOf("_") !== 0 && sorTable[method]) {
288 | return sorTable[method].apply(sorTable, args);
289 | } else {
290 | $.error( 'Method ' + method + ' does not exist on jQuery.sorTable' );
291 | return false;
292 | }
293 | }
294 | });
295 | }
296 | };
297 |
298 | /**
299 | * global sorTable function for initialization
300 | */
301 | $.sorTable = function ( name, initFunc ) {
302 | const sorTables = SORTABLE_MAP[name];
303 | // initFunc is a function -> initialize
304 | if($.isFunction(initFunc)) {
305 | // call init if already initialized
306 | if(sorTables) {
307 | $.each(sorTables, function(){
308 | initFunc(this, $(this.element));
309 | });
310 | }
311 |
312 | // remember for future initializations
313 | SORTABLE_INIT_FUNCTIONS[name] = initFunc;
314 | } else {
315 | // call init if already initialized
316 | if(sorTables) {
317 | const method = initFunc;
318 | const args = Array.prototype.slice.call( arguments, 2 );
319 | $.each(portlets, function(){
320 | this[method].apply(this, args);
321 | });
322 | }
323 | }
324 | };
325 |
326 | })( jQuery, window );
327 |
--------------------------------------------------------------------------------
/controls/jquery.tree.css:
--------------------------------------------------------------------------------
1 | /* tree */
2 | .tree {
3 | display: block;
4 | list-style-type: none;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | .tree li, .tree ul {
10 | display: block;
11 | list-style-type: none;
12 | margin: 0;
13 | padding: 0;
14 | }
15 |
16 | .tree .control span{
17 | cursor:pointer;
18 | display:inline-block;
19 | }
20 |
21 | .tree span {
22 | padding-left: 5px;
23 | padding-right: 5px;
24 | }
25 |
26 | .tree li {
27 | line-height: 20px;
28 | margin-left: 18px;
29 | min-height: 22px;
30 | min-width: 18px;
31 | margin-top: -6px;
32 | white-space: nowrap;
33 | overflow:visible;
34 | }
35 |
36 | .tree > ul > li {
37 | margin-left: 0;
38 | }
39 | .tree ins {
40 | display: inline-block;
41 | height: 18px;
42 | margin-bottom: -4px;
43 | padding: 0;
44 | text-decoration: none;
45 | width: 16px;
46 | position:relative;
47 | cursor: pointer;
48 | }
49 |
50 | .tree li.open > ul {
51 | display: block;
52 | }
53 |
54 | .tree li a {
55 | color: black;
56 | height: 16px;
57 | line-height: 16px;
58 | margin: 0;
59 | text-decoration: none;
60 | white-space: nowrap;
61 | padding: 0 2px;
62 | }
63 |
64 | .tree a > ins {
65 | height: 16px;
66 | width: 16px;
67 | left:0px;
68 | }
69 |
--------------------------------------------------------------------------------
/controls/jquery.tree.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jquery.tree
3 | * -----------
4 | * Simple Tree control
5 | * @version 1.0
6 | * @class
7 | * @author Niko Berger
8 | * @license MIT License GPL
9 | */
10 | ;(function( $, window, undefined ){
11 | "use strict";
12 |
13 | var TREE_INIT_FUNCTIONS = {}, // remember initialization functions
14 | TREE_MAP = {}; // remember all trees
15 |
16 | /**
17 | * @param element {Node} the cotnainer node that should be converted to a tree
18 | * @param options {object} the configuraton object
19 | * @constructor
20 | */
21 | function Tree (element, options) {
22 | // create the options
23 | this.options = $.extend({}, {
24 | /**
25 | * automatically prepend icons
26 | */
27 | autoIcon: false,
28 | /**
29 | * open all levels
30 | */
31 | open: false,
32 | /**
33 | * remember which part of the tree was open in a cookie (non-null = name of the cookie)
34 | */
35 | remember: null,
36 | /**
37 | * field used for id (only required for remembering which node was "open")
38 | */
39 | id: null,
40 | /**
41 | * field used as name, this can also be a function that renders the object
42 | */
43 | name: "name",
44 | /**
45 | * field used for title
46 | */
47 | title: null,
48 | /**
49 | * field to recurse into
50 | */
51 | children: "children",
52 | /**
53 | * set to true to allow multiple elements to be selected
54 | */
55 | multiple: false,
56 | /**
57 | * style when a node is active (null: disables activation)
58 | */
59 | active: "ui-state-active",
60 | hover: "ui-state-hover",
61 |
62 |
63 | /**
64 | * the object used to fill/collect data
65 | */
66 | data: null,
67 |
68 | /**
69 | * true to execute the loaddata automatically
70 | */
71 | load: true,
72 |
73 | /**
74 | * callback function for data fetching
75 | */
76 | loadData: null,
77 |
78 | /**
79 | * callback function when a node is selected
80 | */
81 | select: null,
82 | /**
83 | * callback function when a node is double clicked
84 | */
85 | dblclick: null,
86 |
87 | /**
88 | * callback function when a node is drag/dropped. if null drag/dropping is disabled
89 | */
90 | drop: null,
91 | /**
92 | * optional "root" target which will be visible when dragging starts
93 | */
94 | rootTarget: 'Root '
95 | }, options);
96 |
97 | this.element = element;
98 |
99 | if(this.options.load && this.options.loadData) {
100 | this.options.loadData();
101 | }
102 |
103 | this._init();
104 | }
105 |
106 | /**
107 | * init the portlet - load the config
108 | * @private
109 | */
110 | Tree.prototype._init = function() {
111 | // fill everything
112 | this._repaint();
113 | };
114 |
115 | Tree.prototype._paint = function(basenode, data) {
116 | // return if empty
117 | if(!data || !data.length || data.length == 0)
118 | return;
119 |
120 | var that = this, config = this.options, $this = $(this.element);
121 |
122 | var root = $('');
123 | basenode.append(root);
124 | $.each(data, function(){
125 | var name = null, title = null;
126 | if($.isFunction(config.name)) {
127 | name = config.name(this);
128 | } else {
129 | name = this[config.name];
130 | }
131 |
132 | if($.isFunction(config.title)) {
133 | title = config.title(this);
134 | } else {
135 | title = this[config.title];
136 | }
137 |
138 | var node = $(''+name+' ');
139 | if(title !== null) {
140 | node.attr("title", title);
141 | }
142 |
143 | if(config.id) {
144 | node.data().id = this[config.id];
145 | } else if(this.id)
146 | node.data().id = this.id;
147 |
148 | $("span", node).hover(function(){$(this).addClass(config.hover);}, function(){$(this).removeClass(config.hover);}).click(function(){
149 | if(config.active) {
150 | // deactivate
151 | if(!config.multiple) {
152 | $this.find("." + config.active).removeClass(config.active);
153 | $(this).addClass(config.active);
154 | }
155 | else {
156 | if($(this).hasClass(config.active)) {
157 | $(this).removeClass(config.active);
158 | } else {
159 | $(this).addClass(config.active);
160 | }
161 | }
162 | }
163 |
164 | // trigger selection
165 | if($(this).hasClass(config.active)) {
166 | if(config.select)
167 | config.select($(this).parent().data().node);
168 | $this.data().selected = this;
169 | $this.trigger("select", this);
170 | }
171 | }).dblclick(function(){
172 | // doubleclick: only one secelection
173 | if(config.active) {
174 | $this.find("." + config.active).removeClass(config.active);
175 | $(this).addClass(config.active);
176 | }
177 | // trigger selection
178 | if(config.dblclick)
179 | config.dblclick($(this).parent().data().node);
180 | $this.data().selected = $(this).parent().data().node;
181 | $this.trigger("dblclick", $(this).parent().data().node);
182 | });
183 | node.data().node = this;
184 | root.append(node);
185 | that._paint(node, this[config.children]);
186 | });
187 | };
188 |
189 | /**
190 | * repaint the tree with new data
191 | * @private
192 | */
193 | Tree.prototype._repaint = function() {
194 | this._clear();
195 |
196 | if(this.options.data) {
197 | this._paint($(this.element), this.options.data);
198 | }
199 |
200 | this._enable(this.element, this.options.data);
201 |
202 | if(this.options.drop) {
203 | this._enableSorting();
204 | }
205 | };
206 |
207 | Tree.prototype._enableSorting = function() {
208 | var $this = $(this.element),config = this.options;
209 |
210 | // now add dragndrop capability
211 | $("li", $this).draggable({
212 | opacity: 0.5,
213 | revert:true,
214 | start: function(){
215 | if(config.rootNode) {
216 | config.rootNode.show();
217 | }
218 | },
219 | stop: function() {
220 | if(config.rootNode) {
221 | config.rootNode.hide();
222 | }
223 | }
224 | });
225 |
226 | if(config.rootTarget) {
227 | var node =$(' ');
228 | if($.isFunction(config.rootTarget))
229 | node.children("span").append(config.rootTarget());
230 | else
231 | node.children("span").append(config.rootTarget);
232 | console.log(node.html());
233 | config.rootNode = node;
234 | node.hide();
235 | $this.children("ul.tree").prepend(node);
236 | }
237 |
238 | // and drop them
239 | $("li span", $this).droppable({
240 | tolerance: "pointer",
241 | hoverClass: "ui-state-hover",
242 | drop: function(event, ui){
243 | var dropped = ui.draggable;
244 | var me = $(this).parent();
245 | // reload after move
246 | var meObj = me.data().node;
247 |
248 | // make the change in the pojos
249 | var dropObj = dropped.data().node;
250 |
251 | config.drop(dropObj, meObj);
252 | }
253 | });
254 | };
255 |
256 |
257 | /**
258 | * applies the acutal tree functionality
259 | * @private
260 | */
261 | Tree.prototype._enable = function(base, data) {
262 | var config = this.options;
263 |
264 | // make sure we have openers
265 | $(base).find("li").each(function(){
266 | // avoid double initialization
267 | if($(this).hasClass("tree-item")) {
268 | return;
269 | }
270 | $(this).addClass("tree-item");
271 |
272 | // cleanup
273 | if(config.autoIcon) {
274 | $(this).children("ul").filter( function() {
275 | return $.trim($(this).html()) == '';
276 | }).remove();
277 | }
278 |
279 | // either we have an ul or a method but do not know that there are no childs
280 | var opener = $(' ');
281 | $(this).prepend(opener);
282 | if($(this).children("ul").length > 0 || ($(this).hasClass("portlet-fill") && $(this).data("hasChilds") !== false)) {
283 | opener.addClass("ui-icon ui-icon-triangle-1-e");
284 | } else if(config.empty){
285 | opener.addClass(config.empty);
286 | }
287 |
288 | // in case of ul with li -> open
289 | if($(this).children("ul").length > 0) {
290 | if($(this).find("li").length > 0) {
291 | $(this).addClass("open");
292 | opener.addClass("ui-icon-triangle-1-se").removeClass("ui-icon-triangle-1-e");
293 | }
294 | }
295 |
296 | opener.click(function(){
297 | if($(this).parent().hasClass("open")){
298 | $(this).parent().children("ul").hide();
299 | $(this).parent().removeClass("open");
300 | $(this).addClass("ui-icon-triangle-1-e").removeClass("ui-icon-triangle-1-se");
301 | $(this).parent().trigger("tree.close", [$(this).parent()]);
302 | } else {
303 | $(this).parent().children("ul").show();
304 | $(this).parent().addClass("open");
305 | $(this).addClass("ui-icon-triangle-1-se").removeClass("ui-icon-triangle-1-e");
306 | $(this).parent().trigger("tree.open", [$(this).parent()]);
307 | }
308 | });
309 |
310 | // close ul (do not trigger for dynmic tress -> portlet-fill
311 | if($(this).children("ul").length > 0 && !$(this).parent().hasClass("open") && !config.open) {
312 | opener.trigger("click");
313 | }
314 | });
315 | };
316 |
317 |
318 |
319 | /**
320 | * clear/reset a form. The prefix is normally predefined by init
321 | * @param form the form
322 | * @param prefix the optional prefix used to identify fields for this form
323 | */
324 | Tree.prototype._clear = function() {
325 | $(this.element).html("");
326 | };
327 |
328 |
329 | /**
330 | * repaint the tree with given data
331 | * @param data {object} the data
332 | */
333 | Tree.prototype.fill = function(pojo) {
334 | // clear first
335 | this.clear();
336 | // set the new data
337 | this.options.data = pojo;
338 | // fill everything
339 | this._repaint(this.element, this.options.data);
340 | };
341 |
342 | /**
343 | * Clear all fields in a form
344 | */
345 | Tree.prototype.clear = function() {
346 | // clear first
347 | this._clear();
348 | };
349 |
350 | /**
351 | * @return all currently selected fields
352 | */
353 | Tree.prototype.getAll = function() {
354 | var config = this.options, $this = $(this.element);
355 |
356 | if(!config.active)
357 | return $this.data().selected;
358 |
359 | var ret = [];
360 | $this.find("." + config.active).each(function(){
361 | ret.push($(this).closest("li.tree-item").data().node);
362 | });
363 | return ret;
364 | };
365 |
366 | Tree.prototype.select = function(fields) {
367 | var config = this.options, $this = $(this.element);
368 |
369 | // only if we actually display the select
370 | if(!config.active)
371 | return;
372 |
373 | // deselect all
374 | $this.find("." + config.active).removeClass(config.active);
375 |
376 | // skip if we dont select anything (=clear)
377 | if(!fields)
378 | return;
379 |
380 | $.each(fields, function(){
381 | var id = this;
382 | if(config.id) {
383 | id = this[config.id];
384 | } else if(this.id)
385 | id = this.id;
386 |
387 | $("li.node").each(function(){
388 | if($(this).data().id === id) {
389 | $(this).children("span").addClass(config.active);
390 | return false;
391 | }
392 | });
393 | });
394 | };
395 |
396 | /**
397 | * call the loadData callback or use the current dat aobject to repaint the whole tree
398 | */
399 | Tree.prototype.reload = function(){
400 | if(this.options.loadData)
401 | this.options.loadData();
402 | else {
403 | this.clear();
404 | this._repaint(this.element, this.options.data);
405 | }
406 | };
407 |
408 | /**
409 | * destroy the jsform and its resources.
410 | * @private
411 | */
412 | Tree.prototype.destroy = function( ) {
413 | return $(this.element).each(function(){
414 | $(window).unbind('.tree');
415 | $(this).removeData('tree');
416 | });
417 | };
418 |
419 | // init and call methods
420 | $.fn.tree = function ( method ) {
421 | // Method calling logic
422 | if ( typeof method === 'object' || ! method ) {
423 | return this.each(function () {
424 | if (!$(this).data('tree')) {
425 | $(this).data('tree', new Tree( this, method ));
426 | }
427 | });
428 | } else {
429 | var args = Array.prototype.slice.call( arguments, 1 ),
430 | tree;
431 | // none found
432 | if(this.length === 0) {
433 | return null;
434 | }
435 | // only one - return directly
436 | if(this.length === 1) {
437 | tree = $(this).data('tree');
438 | if (tree) {
439 | if(method.indexOf("_") !== 0 && tree[method]) {
440 | var ret = tree[method].apply(tree, args);
441 | return ret;
442 | }
443 |
444 | $.error( 'Method ' + method + ' does not exist on jQuery.tree' );
445 | return false;
446 | }
447 | }
448 |
449 | return this.each(function () {
450 | tree = $.data(this, 'tree');
451 | if (tree) {
452 | if(method.indexOf("_") !== 0 && tree[method]) {
453 | return tree[method].apply(tree, args);
454 | } else {
455 | $.error( 'Method ' + method + ' does not exist on jQuery.tree' );
456 | return false;
457 | }
458 | }
459 | });
460 | }
461 | };
462 |
463 | /**
464 | * global tree function for initialization
465 | */
466 | $.tree = function ( name, initFunc ) {
467 | var trees = TREE_MAP[name];
468 | // initFunc is a function -> initialize
469 | if($.isFunction(initFunc)) {
470 | // call init if already initialized
471 | if(trees) {
472 | $.each(trees, function(){
473 | initFunc(this, $(this.element));
474 | });
475 | }
476 |
477 | // remember for future initializations
478 | TREE_INIT_FUNCTIONS[name] = initFunc;
479 | } else {
480 | // call init if already initialized
481 | if(trees) {
482 | var method = initFunc;
483 | var args = Array.prototype.slice.call( arguments, 2 );
484 | $.each(portlets, function(){
485 | this[method].apply(this, args);
486 | });
487 | }
488 | }
489 | };
490 |
491 | })( jQuery, window );
492 |
--------------------------------------------------------------------------------
/js/jquery.jsForm.d.ts:
--------------------------------------------------------------------------------
1 | interface JQuery {
2 | /**
3 | * Initialize jsform with default configuration on a node
4 | */
5 | jsForm(): JQuery;
6 | jsForm(func:any): JQuery;
7 | jsForm(func:string, opts:any): JQuery;
8 | }
--------------------------------------------------------------------------------
/jsForm.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jsForm",
3 | "title": "jQuery JSON Form",
4 | "description": "jQuery based form library that allows you to handle data within a js object (i.e. JSON) with html forms.\nYou can modify all fields/properties by simply creating a html form with the correct naming schema (MVVM Pattern).\n\nThe main features of this library are:\n Full standard html with data available in a js object\n Update an existing js object with changes done within a form\n Fully internationalizable with number format, currency and date formatting\n Provide basic functions for formatting (i.e. date/time, money) using html markup\n Provide form validation functionality\n handle collections (arrays) with subobjects\n provides helper methods to handle array manipulation (add new entry/remove an entry, sorting) using only html markup\n Can be used in connection with an autocomplete function to add new array objects",
5 | "keywords": [
6 | "form",
7 | "json",
8 | "data",
9 | "mvvm"
10 | ],
11 | "version": "1.6.0",
12 | "author": {
13 | "name": "Niko Berger"
14 | },
15 | "maintainers": [{
16 | "name": "Niko Berger",
17 | "email": "niko.berger@corinis.com",
18 | "url": "http://www.corinis.com"
19 | }],
20 | "licenses": [{
21 | "type": "MIT",
22 | "url": "https://github.com/corinis/jsForm/MIT-LICENSE.txt"
23 | }],
24 | "bugs": "https://github.com/corinis/jsForm/issues",
25 | "homepage": "https://github.com/corinis/jsForm/",
26 | "docs": "https://github.com/corinis/jsForm/",
27 | "dependencies": {
28 | "jquery": ">=1.7"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/jquery.format-1.3.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery Format Plugin v${version}
3 | * http://www.asual.com/jquery/format/
4 | *
5 | * Copyright (c) 2009-2011 Rostislav Hristov
6 | * Uses code by Matt Kruse
7 | * Dual licensed under the MIT or GPL Version 2 licenses.
8 | * http://jquery.org/license
9 | *
10 | * Date: ${timestamp}
11 | */
12 | (function ($) {
13 |
14 | $.format = (function () {
15 |
16 | var UNDEFINED = 'undefined',
17 | TRUE = true,
18 | FALSE = false,
19 | _locale = {
20 | date: {
21 | format: 'MMM dd, yyyy h:mm:ss a',
22 | monthsFull: ['January','February','March','April','May','June',
23 | 'July','August','September','October','November','December'],
24 | monthsShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
25 | daysFull: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
26 | daysShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
27 | shortDateFormat: 'M/d/yyyy h:mm a',
28 | longDateFormat: 'EEEE, MMMM dd, yyyy h:mm:ss a'
29 | },
30 | number: {
31 | format: '#,##0.0#',
32 | groupingSeparator: ',',
33 | decimalSeparator: '.'
34 | }
35 | };
36 |
37 | return {
38 |
39 | locale: function(value) {
40 | a = {a: 6};
41 | if (value) {
42 | for (var p in value) {
43 | for (var v in value[p]) {
44 | _locale[p][v] = value[p][v];
45 | }
46 | }
47 | }
48 | return _locale;
49 | },
50 |
51 | date: function(value, format) {
52 |
53 | var i = 0,
54 | j = 0,
55 | l = 0,
56 | c = '',
57 | token = '',
58 | x,
59 | y;
60 |
61 | if (typeof value == 'string') {
62 |
63 | var getNumber = function (str, p, minlength, maxlength) {
64 | for (var x = maxlength; x >= minlength; x--) {
65 | var token = str.substring(p, p + x);
66 | if (token.length >= minlength && /^\d+$/.test(token)) {
67 | return token;
68 | }
69 | }
70 | return null;
71 | };
72 |
73 | if (typeof format == UNDEFINED) {
74 | format = _locale.date.format;
75 | }
76 |
77 | var _strict = false,
78 | pos = 0,
79 | now = new Date(0, 0, 0, 0, 0, 0, 0),
80 | year = now.getYear(),
81 | month = now.getMonth() + 1,
82 | date = 1,
83 | hh = now.getHours(),
84 | mm = now.getMinutes(),
85 | ss = now.getSeconds(),
86 | SSS = now.getMilliseconds(),
87 | ampm = '',
88 | monthName,
89 | dayName;
90 |
91 | while (i < format.length) {
92 | token = '';
93 | c = format.charAt(i);
94 | while ((format.charAt(i) == c) && (i < format.length)) {
95 | token += format.charAt(i++);
96 | }
97 | if (token.indexOf('MMMM') > - 1 && token.length > 4) {
98 | token = 'MMMM';
99 | }
100 | if (token.indexOf('EEEE') > - 1 && token.length > 4) {
101 | token = 'EEEE';
102 | }
103 | if (token == 'yyyy' || token == 'yy' || token == 'y') {
104 | if (token == 'yyyy') {
105 | x = 4;
106 | y = 4;
107 | }
108 | if (token == 'yy') {
109 | x = 2;
110 | y = 2;
111 | }
112 | if (token == 'y') {
113 | x = 2;
114 | y = 4;
115 | }
116 | year = getNumber(value, pos, x, y);
117 | if (year === null) {
118 | return 0;
119 | }
120 | pos += year.length;
121 | if (year.length == 2) {
122 | year = parseInt(year, 10);
123 | if (year > 70) {
124 | year = 1900 + year;
125 | } else {
126 | year = 2000 + year;
127 | }
128 | }
129 | } else if (token == 'MMMM'){
130 | month = 0;
131 | for (j = 0, l = _locale.date.monthsFull.length; j < l; j++) {
132 | monthName = _locale.date.monthsFull[j];
133 | if (value.substring(pos, pos + monthName.length).toLowerCase() == monthName.toLowerCase()) {
134 | month = j + 1;
135 | pos += monthName.length;
136 | break;
137 | }
138 | }
139 | if ((month < 1) || (month > 12)){
140 | return 0;
141 | }
142 | } else if (token == 'MMM'){
143 | month = 0;
144 | for (j = 0, l = _locale.date.monthsShort.length; j < l; j++) {
145 | monthName = _locale.date.monthsShort[j];
146 | if (value.substring(pos, pos + monthName.length).toLowerCase() == monthName.toLowerCase()) {
147 | month = j + 1;
148 | pos += monthName.length;
149 | break;
150 | }
151 | }
152 | if ((month < 1) || (month > 12)){
153 | return 0;
154 | }
155 | } else if (token == 'EEEE'){
156 | for (j = 0, l = _locale.date.daysFull.length; j < l; j++) {
157 | dayName = _locale.date.daysFull[j];
158 | if (value.substring(pos, pos + dayName.length).toLowerCase() == dayName.toLowerCase()) {
159 | pos += dayName.length;
160 | break;
161 | }
162 | }
163 | } else if (token == 'EEE'){
164 | for (j = 0, l =_locale.date.daysShort.length; j < l; j++) {
165 | dayName = _locale.date.daysShort[j];
166 | if (value.substring(pos, pos + dayName.length).toLowerCase() == dayName.toLowerCase()) {
167 | pos += dayName.length;
168 | break;
169 | }
170 | }
171 | } else if (token == 'MM' || token == 'M') {
172 | month = getNumber(value, pos, _strict ? token.length : 1, 2);
173 | if (month === null || (month < 1) || (month > 12)){
174 | return 0;
175 | }
176 | pos += month.length;
177 | } else if (token == 'dd' || token == 'd') {
178 | date = getNumber(value, pos, _strict ? token.length : 1, 2);
179 | if (date === null || (date < 1) || (date > 31)){
180 | return 0;
181 | }
182 | pos += date.length;
183 | } else if (token == 'hh' || token == 'h') {
184 | hh = getNumber(value, pos, _strict ? token.length : 1, 2);
185 | if (hh === null || (hh < 1) || (hh > 12)) {
186 | return 0;
187 | }
188 | pos += hh.length;
189 | } else if (token == 'HH' || token == 'H') {
190 | hh = getNumber(value, pos, _strict ? token.length : 1, 2);
191 | if(hh === null || (hh < 0) || (hh > 23)){
192 | return 0;
193 | }
194 | pos += hh.length;
195 | } else if (token == 'KK' || token == 'K') {
196 | hh = getNumber(value, pos, _strict ? token.length : 1, 2);
197 | if (hh === null || (hh < 0) || (hh > 11)){
198 | return 0;
199 | }
200 | pos += hh.length;
201 | } else if (token == 'kk' || token == 'k') {
202 | hh = getNumber(value, pos, _strict ? token.length : 1, 2);
203 | if (hh === null || (hh < 1) || (hh > 24)){
204 | return 0;
205 | }
206 | pos += hh.length;
207 | hh--;
208 | } else if (token == 'mm' || token == 'm') {
209 | mm = getNumber(value, pos, _strict ? token.length : 1, 2);
210 | if (mm === null || (mm < 0) || ( mm > 59)) {
211 | return 0;
212 | }
213 | pos += mm.length;
214 | } else if (token == 'ss' || token == 's') {
215 | ss = getNumber(value, pos, _strict ? token.length : 1, 2);
216 | if (ss === null || (ss < 0) || (ss > 59)){
217 | return 0;
218 | }
219 | pos += ss.length;
220 | } else if (token == 'SSS' || token == 'SS' || token == 'S') {
221 | SSS = getNumber(value, pos, _strict ? token.length : 1, 3);
222 | if (SSS === null || (SSS < 0) || (SSS > 999)){
223 | return 0;
224 | }
225 | pos += SSS.length;
226 | } else if (token == 'a') {
227 | var ap = value.substring(pos, pos + 2).toLowerCase();
228 | if (ap == 'am') {
229 | ampm = 'AM';
230 | } else if (ap == 'pm') {
231 | ampm = 'PM';
232 | } else {
233 | return 0;
234 | }
235 | pos += 2;
236 | } else {
237 | if (token != value.substring(pos, pos + token.length)) {
238 | return 0;
239 | } else {
240 | pos += token.length;
241 | }
242 | }
243 | }
244 | if (pos != value.length) {
245 | return 0;
246 | }
247 | if (month == 2) {
248 | if (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) {
249 | if (date > 29) {
250 | return 0;
251 | }
252 | } else {
253 | if (date > 28) {
254 | return 0;
255 | }
256 | }
257 | }
258 | if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) {
259 | if (date > 30) {
260 | return 0;
261 | }
262 | }
263 | if (hh < 12 && ampm == 'PM') {
264 | hh = hh - 0 + 12;
265 | } else if (hh > 11 && ampm == 'AM') {
266 | hh -= 12;
267 | }
268 |
269 | return (new Date(year, month - 1, date, hh, mm, ss, SSS));
270 |
271 | } else {
272 |
273 | var formatNumber = function (n, s) {
274 | if (typeof s == UNDEFINED || s == 2) {
275 | return (n >= 0 && n < 10 ? '0' : '') + n;
276 | } else {
277 | if (n >= 0 && n < 10) {
278 | return '00' + n;
279 | }
280 | if (n >= 10 && n <100) {
281 | return '0' + n;
282 | }
283 | return n;
284 | }
285 | };
286 |
287 | if (typeof format == UNDEFINED) {
288 | format = _locale.date.format;
289 | }
290 |
291 | y = value.getYear();
292 | if (y < 1000) {
293 | y = String(y + 1900);
294 | }
295 |
296 | var M = value.getMonth() + 1,
297 | d = value.getDate(),
298 | E = value.getDay(),
299 | H = value.getHours(),
300 | m = value.getMinutes(),
301 | s = value.getSeconds(),
302 | S = value.getMilliseconds();
303 |
304 | value = {
305 | y: y,
306 | yyyy: y,
307 | yy: String(y).substring(2, 4),
308 | M: M,
309 | MM: formatNumber(M),
310 | MMM: _locale.date.monthsShort[M-1],
311 | MMMM: _locale.date.monthsFull[M-1],
312 | d: d,
313 | dd: formatNumber(d),
314 | EEE: _locale.date.daysShort[E],
315 | EEEE: _locale.date.daysFull[E],
316 | H: H,
317 | HH: formatNumber(H)
318 | };
319 |
320 | if (H === 0) {
321 | value.h = 12;
322 | } else if (H > 12) {
323 | value.h = H - 12;
324 | } else {
325 | value.h = H;
326 | }
327 |
328 | value.hh = formatNumber(value.h);
329 | value.k = H !== 0 ? H : 24;
330 | value.kk = formatNumber(value.k);
331 |
332 | if (H > 11) {
333 | value.K = H - 12;
334 | } else {
335 | value.K = H;
336 | }
337 |
338 | value.KK = formatNumber(value.K);
339 |
340 | if (H > 11) {
341 | value.a = 'PM';
342 | } else {
343 | value.a = 'AM';
344 | }
345 |
346 | value.m = m;
347 | value.mm = formatNumber(m);
348 | value.s = s;
349 | value.ss = formatNumber(s);
350 | value.S = S;
351 | value.SS = formatNumber(S);
352 | value.SSS = formatNumber(S, 3);
353 |
354 | var result = '';
355 |
356 | i = 0;
357 | c = '';
358 | token = '';
359 | s = false;
360 |
361 | while (i < format.length) {
362 | token = '';
363 | c = format.charAt(i);
364 | if (c == '\'') {
365 | i++;
366 | if (format.charAt(i) == c) {
367 | result = result + c;
368 | i++;
369 | } else {
370 | s = !s;
371 | }
372 | } else {
373 | while (format.charAt(i) == c) {
374 | token += format.charAt(i++);
375 | }
376 | if (token.indexOf('MMMM') != -1 && token.length > 4) {
377 | token = 'MMMM';
378 | }
379 | if (token.indexOf('EEEE') != -1 && token.length > 4) {
380 | token = 'EEEE';
381 | }
382 | if (typeof value[token] != UNDEFINED && !s) {
383 | result = result + value[token];
384 | } else {
385 | result = result + token;
386 | }
387 | }
388 | }
389 | return result;
390 | }
391 | },
392 |
393 | number: function(value, format) {
394 |
395 | var groupingSeparator,
396 | groupingIndex,
397 | decimalSeparator,
398 | decimalIndex,
399 | roundFactor,
400 | result,
401 | i;
402 |
403 | if (typeof value == 'string') {
404 |
405 | groupingSeparator = _locale.number.groupingSeparator;
406 | decimalSeparator = _locale.number.decimalSeparator;
407 | decimalIndex = value.indexOf(decimalSeparator);
408 |
409 | roundFactor = 1;
410 |
411 | if (decimalIndex != -1) {
412 | roundFactor = Math.pow(10, value.length - decimalIndex - 1);
413 | }
414 |
415 | value = value.replace(new RegExp('[' + groupingSeparator + ']', 'g'), '');
416 | value = value.replace(new RegExp('[' + decimalSeparator + ']'), '.');
417 |
418 | return Math.round(value*roundFactor)/roundFactor;
419 |
420 | } else {
421 |
422 | if (typeof format == UNDEFINED || format.length < 1) {
423 | format = _locale.number.format;
424 | }
425 |
426 | groupingSeparator = ',';
427 | groupingIndex = format.lastIndexOf(groupingSeparator);
428 | decimalSeparator = '.';
429 | decimalIndex = format.indexOf(decimalSeparator);
430 |
431 | var integer = '',
432 | fraction = '',
433 | negative = value < 0,
434 | minFraction = format.substr(decimalIndex + 1).replace(/#/g, '').length,
435 | maxFraction = format.substr(decimalIndex + 1).length,
436 | powFraction = 10;
437 |
438 | value = Math.abs(value);
439 |
440 | if (decimalIndex != -1) {
441 | fraction = _locale.number.decimalSeparator;
442 | if (maxFraction > 0) {
443 | roundFactor = 1000;
444 | powFraction = Math.pow(powFraction, maxFraction);
445 | var tempRound = Math.round(parseInt(value * powFraction * roundFactor -
446 | Math.round(value) * powFraction * roundFactor, 10) / roundFactor),
447 | tempFraction = String(tempRound < 0 ? Math.round(parseInt(value * powFraction * roundFactor -
448 | parseInt(value, 10) * powFraction * roundFactor, 10) / roundFactor) : tempRound),
449 | parts = value.toString().split('.');
450 | if (typeof parts[1] != UNDEFINED) {
451 | for (i = 0; i < maxFraction; i++) {
452 | if (parts[1].substr(i, 1) == '0' && i < maxFraction - 1 &&
453 | tempFraction.length != maxFraction) {
454 | tempFraction = '0' + tempFraction;
455 | } else {
456 | break;
457 | }
458 | }
459 | }
460 | for (i = 0; i < (maxFraction - fraction.length); i++) {
461 | tempFraction += '0';
462 | }
463 | var symbol,
464 | formattedFraction = '';
465 | for (i = 0; i < tempFraction.length; i++) {
466 | symbol = tempFraction.substr(i, 1);
467 | if (i >= minFraction && symbol == '0' && /^0*$/.test(tempFraction.substr(i+1))) {
468 | break;
469 | }
470 | formattedFraction += symbol;
471 | }
472 | fraction += formattedFraction;
473 | }
474 | if (fraction == _locale.number.decimalSeparator) {
475 | fraction = '';
476 | }
477 | }
478 |
479 | if (decimalIndex !== 0) {
480 | if (fraction != '') {
481 | integer = String(parseInt(Math.round(value * powFraction) / powFraction, 10));
482 | } else {
483 | integer = String(Math.round(value));
484 | }
485 | var grouping = _locale.number.groupingSeparator,
486 | groupingSize = 0;
487 | if (groupingIndex != -1) {
488 | if (decimalIndex != -1) {
489 | groupingSize = decimalIndex - groupingIndex;
490 | } else {
491 | groupingSize = format.length - groupingIndex;
492 | }
493 | groupingSize--;
494 | }
495 | if (groupingSize > 0) {
496 | var count = 0,
497 | formattedInteger = '';
498 | i = integer.length;
499 | while (i--) {
500 | if (count !== 0 && count % groupingSize === 0) {
501 | formattedInteger = grouping + formattedInteger;
502 | }
503 | formattedInteger = integer.substr(i, 1) + formattedInteger;
504 | count++;
505 | }
506 | integer = formattedInteger;
507 | }
508 | var maxInteger, maxRegExp = /#|,/g;
509 | if (decimalIndex != -1) {
510 | maxInteger = format.substr(0, decimalIndex).replace(maxRegExp, '').length;
511 | } else {
512 | maxInteger = format.replace(maxRegExp, '').length;
513 | }
514 | var tempInteger = integer.length;
515 | for (i = tempInteger; i < maxInteger; i++) {
516 | integer = '0' + integer;
517 | }
518 | }
519 | result = integer + fraction;
520 | return (negative ? '-' : '') + result;
521 | }
522 | }
523 | };
524 | })();
525 |
526 | }(jQuery));
--------------------------------------------------------------------------------
/lib/jquery.simpledateformat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jquery.simpledateformat.js
3 | * Date and time patterns
4 | *
5 | * yy = short year
6 | * yyyy = long year
7 | * M = month (1-12)
8 | * MM = month (01-12)
9 | * MMM = month abbreviation (Jan, Feb … Dec)
10 | * MMMM = long month (January, February … December)
11 | * d = day (1 - 31)
12 | * dd = day (01 - 31)
13 | * ddd = day of the week in words (Monday, Tuesday … Sunday)
14 | * h = hour in am/pm (0-12)
15 | * hh = hour in am/pm (00-12)
16 | * HH = hour in day (00-23)
17 | * mm = minute
18 | * ss = second
19 | * SSS = milliseconds
20 | * a = am/pm marker
21 | *
22 | * Usage
23 | * $.simpledateformat.format(new Date(), "dd/MM/yyyy hh:mm:ss");
24 | * $.simpledateformat.format(100, "hh:mm:ss");
25 | * @based https://github.com/phstc/jquery-dateFormat
26 | * @license MIT License GPL
27 | * @contributor albertjan, christopherstott, cipa, dahdread, docchang, eemeyer, gwilson2151, jafin, jakemonO, jharting, kitto, larryzhao, leesolutions, nashg842, fuzzygroove, stuttufu, thiloplanz, Zyber17.
28 | */
29 | "use strict";
30 |
31 | ;(function ($) {
32 |
33 | var daysInWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
34 | var shortMonthsInYear = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
35 | var longMonthsInYear = ["January", "February", "March", "April", "May", "June",
36 | "July", "August", "September", "October", "November", "December"];
37 | var shortMonthsToNumber = [];
38 | shortMonthsToNumber["Jan"] = "01";
39 | shortMonthsToNumber["Feb"] = "02";
40 | shortMonthsToNumber["Mar"] = "03";
41 | shortMonthsToNumber["Apr"] = "04";
42 | shortMonthsToNumber["May"] = "05";
43 | shortMonthsToNumber["Jun"] = "06";
44 | shortMonthsToNumber["Jul"] = "07";
45 | shortMonthsToNumber["Aug"] = "08";
46 | shortMonthsToNumber["Sep"] = "09";
47 | shortMonthsToNumber["Oct"] = "10";
48 | shortMonthsToNumber["Nov"] = "11";
49 | shortMonthsToNumber["Dec"] = "12";
50 |
51 | $.simpledateformat = (function () {
52 | function strDay(value) {
53 | return daysInWeek[parseInt(value, 10)] || value;
54 | }
55 |
56 | function strMonth(value) {
57 | var monthArrayIndex = parseInt(value, 10) - 1;
58 | return shortMonthsInYear[monthArrayIndex] || value;
59 | }
60 |
61 | function strLongMonth(value) {
62 | var monthArrayIndex = parseInt(value, 10) - 1;
63 | return longMonthsInYear[monthArrayIndex] || value;
64 | }
65 |
66 | var parseMonth = function (value) {
67 | return shortMonthsToNumber[value] || value;
68 | };
69 |
70 | var parseTime = function (value) {
71 | var retValue = value;
72 | var millis = "";
73 | if (retValue.indexOf(".") !== -1) {
74 | var delimited = retValue.split('.');
75 | retValue = delimited[0];
76 | millis = delimited[1];
77 | }
78 |
79 | var values3 = retValue.split(":");
80 |
81 | if (values3.length === 3) {
82 | return {
83 | time: retValue,
84 | hour: values3[0],
85 | minute: values3[1],
86 | second: values3[2],
87 | millis: millis
88 | };
89 | } else {
90 | return {
91 | time: "",
92 | hour: "",
93 | minute: "",
94 | second: "",
95 | millis: ""
96 | };
97 | }
98 | };
99 |
100 | return {
101 | format: function (value, format) {
102 | /*
103 | value = new java.util.Date()
104 | 2009-12-18 10:54:50.546
105 | */
106 | try {
107 | var date = null;
108 | var year = null;
109 | var month = null;
110 | var dayOfMonth = null;
111 | var dayOfWeek = null;
112 | var time = null;
113 |
114 | // convert number to date
115 | if (typeof value == "number"){
116 | value = new Date(value);
117 | }
118 |
119 | if ($.isFunction(value.getFullYear)) {
120 | year = value.getFullYear();
121 | month = value.getMonth() + 1;
122 | dayOfMonth = value.getDate();
123 | dayOfWeek = value.getDay();
124 | time = {
125 | time: "",
126 | hour: value.getHours(),
127 | minute: value.getMinutes(),
128 | second: value.getSeconds(),
129 | millis: value.getMilliseconds()
130 | };
131 | } else if (value.search(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.?\d{0,3}[Z\-+]?(\d{2}:?\d{2})?/) != -1) {
132 | /* 2009-04-19T16:11:05+02:00 || 2009-04-19T16:11:05Z */
133 | var values = value.split(/[T\+-]/);
134 | year = values[0];
135 | month = values[1];
136 | dayOfMonth = values[2];
137 | time = parseTime(values[3].split(".")[0]);
138 | date = new Date(year, month - 1, dayOfMonth);
139 | dayOfWeek = date.getDay();
140 | } else {
141 | var values = value.split(" ");
142 | switch (values.length) {
143 | case 6:
144 | /* Wed Jan 13 10:43:41 CET 2010 */
145 | year = values[5];
146 | month = parseMonth(values[1]);
147 | dayOfMonth = values[2];
148 | time = parseTime(values[3]);
149 | date = new Date(year, month - 1, dayOfMonth);
150 | dayOfWeek = date.getDay();
151 | break;
152 | case 2:
153 | /* 2009-12-18 10:54:50.546 */
154 | var values2 = values[0].split("-");
155 | year = values2[0];
156 | month = values2[1];
157 | dayOfMonth = values2[2];
158 | time = parseTime(values[1]);
159 | date = new Date(year, month - 1, dayOfMonth);
160 | dayOfWeek = date.getDay();
161 | break;
162 | case 7:
163 | /* Tue Mar 01 2011 12:01:42 GMT-0800 (PST) */
164 | case 9:
165 | /*added by Larry, for Fri Apr 08 2011 00:00:00 GMT+0800 (China Standard Time) */
166 | case 10:
167 | /* added by Larry, for Fri Apr 08 2011 00:00:00 GMT+0200 (W. Europe Daylight Time) */
168 | year = values[3];
169 | month = parseMonth(values[1]);
170 | dayOfMonth = values[2];
171 | time = parseTime(values[4]);
172 | date = new Date(year, month - 1, dayOfMonth);
173 | dayOfWeek = date.getDay();
174 | break;
175 | case 1:
176 | /* added by Jonny, for 2012-02-07CET00:00:00 (Doctrine Entity -> Json Serializer) */
177 | var values2 = values[0].split("");
178 | year=values2[0]+values2[1]+values2[2]+values2[3];
179 | month= values2[5]+values2[6];
180 | dayOfMonth = values2[8]+values2[9];
181 | time = parseTime(values2[13]+values2[14]+values2[15]+values2[16]+values2[17]+values2[18]+values2[19]+values2[20]);
182 | date = new Date(year, month - 1, dayOfMonth);
183 | dayOfWeek = date.getDay();
184 | break;
185 | default:
186 | return value;
187 | }
188 | }
189 |
190 | var pattern = "";
191 | var retValue = "";
192 | var unparsedRest = "";
193 | /*
194 | Issue 1 - variable scope issue in format.date
195 | Thanks jakemonO
196 | */
197 | for (var i = 0; i < format.length; i++) {
198 | var currentPattern = format.charAt(i);
199 |
200 | pattern += currentPattern;
201 | unparsedRest = "";
202 | switch (pattern) {
203 | case "ddd":
204 | retValue += strDay(dayOfWeek);
205 | pattern = "";
206 | break;
207 | case "dd":
208 | if (format.charAt(i + 1) == "d") {
209 | break;
210 | }
211 | if (String(dayOfMonth).length === 1) {
212 | dayOfMonth = '0' + dayOfMonth;
213 | }
214 | retValue += dayOfMonth;
215 | pattern = "";
216 | break;
217 | case "d":
218 | if (format.charAt(i + 1) == "d") {
219 | break;
220 | }
221 | retValue += parseInt(dayOfMonth, 10);
222 | pattern = "";
223 | break;
224 | case "MMMM":
225 | retValue += strLongMonth(month);
226 | pattern = "";
227 | break;
228 | case "MMM":
229 | if (format.charAt(i + 1) === "M") {
230 | break;
231 | }
232 | retValue += strMonth(month);
233 | pattern = "";
234 | break;
235 | case "MM":
236 | if (format.charAt(i + 1) == "M") {
237 | break;
238 | }
239 | if (String(month).length === 1) {
240 | month = '0' + month;
241 | }
242 | retValue += month;
243 | pattern = "";
244 | break;
245 | case "M":
246 | if (format.charAt(i + 1) == "M") {
247 | break;
248 | }
249 | retValue += parseInt(month, 10);
250 | pattern = "";
251 | break;
252 | case "yyyy":
253 | retValue += year;
254 | pattern = "";
255 | break;
256 | case "yy":
257 | if (format.charAt(i + 1) == "y" &&
258 | format.charAt(i + 2) == "y") {
259 | break;
260 | }
261 | retValue += String(year).slice(-2);
262 | pattern = "";
263 | break;
264 | case "HH":
265 | case "hh":
266 | /* time.hour is "00" as string == is used instead of === */
267 | var hour = (time.hour == 0 ? 12 : time.hour < 13 ? time.hour : time.hour - 12);
268 | hour = hour < 10 ? '0' + hour : hour;
269 | retValue += hour;
270 | pattern = "";
271 | break;
272 | case "h":
273 | if (format.charAt(i + 1) == "h") {
274 | break;
275 | }
276 | var hour = (time.hour == 0 ? 12 : time.hour < 13 ? time.hour : time.hour - 12);
277 | retValue += hour;
278 | // Fixing issue https://github.com/phstc/jquery-dateFormat/issues/21
279 | // retValue = parseInt(retValue, 10);
280 | pattern = "";
281 | break;
282 | case "mm":
283 | retValue += (time.minute < 10 ? '0' : '') + time.minute;
284 | pattern = "";
285 | break;
286 | case "ss":
287 | /* ensure only seconds are added to the return string */
288 | retValue += (time.second < 10 ? '0' : '') + time.second.substring(0, 2);
289 | pattern = "";
290 | break;
291 | case "SSS":
292 | retValue += time.millis.substring(0, 3);
293 | pattern = "";
294 | break;
295 | case "a":
296 | retValue += time.hour >= 12 ? "PM" : "AM";
297 | pattern = "";
298 | break;
299 | case " ":
300 | retValue += currentPattern;
301 | pattern = "";
302 | break;
303 | case ".":
304 | retValue += currentPattern;
305 | pattern = "";
306 | break;
307 | case "/":
308 | retValue += currentPattern;
309 | pattern = "";
310 | break;
311 | case ":":
312 | retValue += currentPattern;
313 | pattern = "";
314 | break;
315 | default:
316 | if (pattern.length === 2 && pattern.indexOf("y") !== 0 && pattern != "SS") {
317 | retValue += pattern.substring(0, 1);
318 | pattern = pattern.substring(1, 2);
319 | } else if ((pattern.length === 3 && pattern.indexOf("yyy") === -1)) {
320 | pattern = "";
321 | } else {
322 | unparsedRest = pattern;
323 | }
324 | }
325 | }
326 | retValue += unparsedRest;
327 | return retValue;
328 | } catch (e) {
329 | console.log(e);
330 | return value;
331 | }
332 | }
333 | };
334 | }());
335 | }(jQuery));
--------------------------------------------------------------------------------
/lib/jquery.tagit.css:
--------------------------------------------------------------------------------
1 | ul.tagit {
2 | padding: 1px 5px;
3 | overflow: auto;
4 | margin-left: inherit; /* usually we don't want the regular ul margins. */
5 | margin-right: inherit;
6 | }
7 | ul.tagit li {
8 | display: block;
9 | float: left;
10 | margin: 2px 5px 2px 0;
11 | }
12 | ul.tagit li.tagit-choice {
13 | position: relative;
14 | line-height: inherit;
15 | }
16 |
17 | ul.tagit li.tagit-choice-read-only {
18 | padding: .2em .5em .2em .5em;
19 | }
20 |
21 | ul.tagit li.tagit-choice-editable {
22 | padding: .2em 18px .2em .5em;
23 | }
24 |
25 | ul.tagit li.tagit-new {
26 | padding: .25em 4px .25em 0;
27 | }
28 |
29 | ul.tagit li.tagit-choice a.tagit-label {
30 | cursor: pointer;
31 | text-decoration: none;
32 | }
33 | ul.tagit li.tagit-choice .tagit-close {
34 | cursor: pointer;
35 | position: absolute;
36 | right: .1em;
37 | top: 50%;
38 | margin-top: -8px;
39 | line-height: 17px;
40 | }
41 |
42 | /* used for some custom themes that don't need image icons */
43 | ul.tagit li.tagit-choice .tagit-close .text-icon {
44 | display: none;
45 | }
46 |
47 | ul.tagit li.tagit-choice input {
48 | display: block;
49 | float: left;
50 | margin: 2px 5px 2px 0;
51 | }
52 | ul.tagit input[type="text"] {
53 | -moz-box-sizing: border-box;
54 | -webkit-box-sizing: border-box;
55 | box-sizing: border-box;
56 |
57 | -moz-box-shadow: none;
58 | -webkit-box-shadow: none;
59 | box-shadow: none;
60 |
61 | border: none;
62 | margin: 0;
63 | padding: 0;
64 | width: inherit;
65 | background-color: inherit;
66 | outline: none;
67 | }
68 |
--------------------------------------------------------------------------------
/lib/tag-it.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Tag-it!
3 | *
4 | * @version v2.0 (06/2011)
5 | *
6 | * Copyright 2011, Levy Carneiro Jr.
7 | * Released under the MIT license.
8 | * http://aehlke.github.com/tag-it/LICENSE
9 | *
10 | * Homepage:
11 | * http://aehlke.github.com/tag-it/
12 | *
13 | * Authors:
14 | * Levy Carneiro Jr.
15 | * Martin Rehfeld
16 | * Tobias Schmidt
17 | * Skylar Challand
18 | * Alex Ehlke
19 | *
20 | * Maintainer:
21 | * Alex Ehlke - Twitter: @aehlke
22 | *
23 | * Dependencies:
24 | * jQuery v1.4+
25 | * jQuery UI v1.8+
26 | */
27 | (function($) {
28 |
29 | $.widget('ui.tagit', {
30 | options: {
31 | allowDuplicates : false,
32 | caseSensitive : true,
33 | fieldName : 'tags',
34 | placeholderText : null, // Sets `placeholder` attr on input field.
35 | readOnly : false, // Disables editing.
36 | removeConfirmation: false, // Require confirmation to remove tags.
37 | tagLimit : null, // Max number of tags allowed (null for unlimited).
38 |
39 | // Used for autocomplete, unless you override `autocomplete.source`.
40 | availableTags : [],
41 |
42 | // Use to override or add any options to the autocomplete widget.
43 | //
44 | // By default, autocomplete.source will map to availableTags,
45 | // unless overridden.
46 | autocomplete: {},
47 |
48 | // Shows autocomplete before the user even types anything.
49 | showAutocompleteOnFocus: false,
50 |
51 | // When enabled, quotes are unneccesary for inputting multi-word tags.
52 | allowSpaces: false,
53 |
54 | // The below options are for using a single field instead of several
55 | // for our form values.
56 | //
57 | // When enabled, will use a single hidden field for the form,
58 | // rather than one per tag. It will delimit tags in the field
59 | // with singleFieldDelimiter.
60 | //
61 | // The easiest way to use singleField is to just instantiate tag-it
62 | // on an INPUT element, in which case singleField is automatically
63 | // set to true, and singleFieldNode is set to that element. This
64 | // way, you don't need to fiddle with these options.
65 | singleField: false,
66 |
67 | // This is just used when preloading data from the field, and for
68 | // populating the field with delimited tags as the user adds them.
69 | singleFieldDelimiter: ',',
70 |
71 | // Set this to an input DOM node to use an existing form field.
72 | // Any text in it will be erased on init. But it will be
73 | // populated with the text of tags as they are created,
74 | // delimited by singleFieldDelimiter.
75 | //
76 | // If this is not set, we create an input node for it,
77 | // with the name given in settings.fieldName.
78 | singleFieldNode: null,
79 |
80 | // Whether to animate tag removals or not.
81 | animate: true,
82 |
83 | // Optionally set a tabindex attribute on the input that gets
84 | // created for tag-it.
85 | tabIndex: null,
86 |
87 | // Event callbacks.
88 | beforeTagAdded : null,
89 | afterTagAdded : null,
90 |
91 | beforeTagRemoved : null,
92 | afterTagRemoved : null,
93 |
94 | onTagClicked : null,
95 | onTagLimitExceeded : null,
96 |
97 |
98 | // DEPRECATED:
99 | //
100 | // /!\ These event callbacks are deprecated and WILL BE REMOVED at some
101 | // point in the future. They're here for backwards-compatibility.
102 | // Use the above before/after event callbacks instead.
103 | onTagAdded : null,
104 | onTagRemoved: null,
105 | // `autocomplete.source` is the replacement for tagSource.
106 | tagSource: null
107 | // Do not use the above deprecated options.
108 | },
109 |
110 | _create: function() {
111 | // for handling static scoping inside callbacks
112 | var that = this;
113 |
114 | // There are 2 kinds of DOM nodes this widget can be instantiated on:
115 | // 1. UL, OL, or some element containing either of these.
116 | // 2. INPUT, in which case 'singleField' is overridden to true,
117 | // a UL is created and the INPUT is hidden.
118 | if (this.element.is('input')) {
119 | this.tagList = $('').insertAfter(this.element);
120 | this.options.singleField = true;
121 | this.options.singleFieldNode = this.element;
122 | this.element.css('display', 'none');
123 | } else {
124 | this.tagList = this.element.find('ul, ol').andSelf().last();
125 | }
126 |
127 | this.tagInput = $(' ').addClass('ui-widget-content');
128 |
129 | if (this.options.readOnly) this.tagInput.attr('disabled', 'disabled');
130 |
131 | if (this.options.tabIndex) {
132 | this.tagInput.attr('tabindex', this.options.tabIndex);
133 | }
134 |
135 | if (this.options.placeholderText) {
136 | this.tagInput.attr('placeholder', this.options.placeholderText);
137 | }
138 |
139 | if (!this.options.autocomplete.source) {
140 | this.options.autocomplete.source = function(search, showChoices) {
141 | var filter = search.term.toLowerCase();
142 | var choices = $.grep(this.options.availableTags, function(element) {
143 | // Only match autocomplete options that begin with the search term.
144 | // (Case insensitive.)
145 | return (element.toLowerCase().indexOf(filter) === 0);
146 | });
147 | if (!this.options.allowDuplicates) {
148 | choices = this._subtractArray(choices, this.assignedTags());
149 | }
150 | showChoices(choices);
151 | };
152 | }
153 |
154 | if (this.options.showAutocompleteOnFocus) {
155 | this.tagInput.focus(function(event, ui) {
156 | that._showAutocomplete();
157 | });
158 |
159 | if (typeof this.options.autocomplete.minLength === 'undefined') {
160 | this.options.autocomplete.minLength = 0;
161 | }
162 | }
163 |
164 | // Bind autocomplete.source callback functions to this context.
165 | if ($.isFunction(this.options.autocomplete.source)) {
166 | this.options.autocomplete.source = $.proxy(this.options.autocomplete.source, this);
167 | }
168 |
169 | // DEPRECATED.
170 | if ($.isFunction(this.options.tagSource)) {
171 | this.options.tagSource = $.proxy(this.options.tagSource, this);
172 | }
173 |
174 | this.tagList
175 | .addClass('tagit')
176 | .addClass('ui-widget ui-widget-content ui-corner-all')
177 | // Create the input field.
178 | .append($(' ').append(this.tagInput))
179 | .click(function(e) {
180 | var target = $(e.target);
181 | if (target.hasClass('tagit-label')) {
182 | var tag = target.closest('.tagit-choice');
183 | if (!tag.hasClass('removed')) {
184 | that._trigger('onTagClicked', e, {tag: tag, tagLabel: that.tagLabel(tag)});
185 | }
186 | } else {
187 | // Sets the focus() to the input field, if the user
188 | // clicks anywhere inside the UL. This is needed
189 | // because the input field needs to be of a small size.
190 | that.tagInput.focus();
191 | }
192 | });
193 |
194 | // Single field support.
195 | var addedExistingFromSingleFieldNode = false;
196 | if (this.options.singleField) {
197 | if (this.options.singleFieldNode) {
198 | // Add existing tags from the input field.
199 | var node = $(this.options.singleFieldNode);
200 | var tags = node.val().split(this.options.singleFieldDelimiter);
201 | node.val('');
202 | $.each(tags, function(index, tag) {
203 | that.createTag(tag, null, true);
204 | addedExistingFromSingleFieldNode = true;
205 | });
206 | } else {
207 | // Create our single field input after our list.
208 | this.options.singleFieldNode = $(' ');
209 | this.tagList.after(this.options.singleFieldNode);
210 | }
211 | }
212 |
213 | // Add existing tags from the list, if any.
214 | if (!addedExistingFromSingleFieldNode) {
215 | this.tagList.children('li').each(function() {
216 | if (!$(this).hasClass('tagit-new')) {
217 | that.createTag($(this).text(), $(this).attr('class'), true);
218 | $(this).remove();
219 | }
220 | });
221 | }
222 |
223 | // Events.
224 | this.tagInput
225 | .keydown(function(event) {
226 | // Backspace is not detected within a keypress, so it must use keydown.
227 | if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') {
228 | var tag = that._lastTag();
229 | if (!that.options.removeConfirmation || tag.hasClass('remove')) {
230 | // When backspace is pressed, the last tag is deleted.
231 | that.removeTag(tag);
232 | } else if (that.options.removeConfirmation) {
233 | tag.addClass('remove ui-state-highlight');
234 | }
235 | } else if (that.options.removeConfirmation) {
236 | that._lastTag().removeClass('remove ui-state-highlight');
237 | }
238 |
239 | // Comma/Space/Enter are all valid delimiters for new tags,
240 | // except when there is an open quote or if setting allowSpaces = true.
241 | // Tab will also create a tag, unless the tag input is empty,
242 | // in which case it isn't caught.
243 | if (
244 | event.which === $.ui.keyCode.COMMA ||
245 | event.which === $.ui.keyCode.ENTER ||
246 | (
247 | event.which == $.ui.keyCode.TAB &&
248 | that.tagInput.val() !== ''
249 | ) ||
250 | (
251 | event.which == $.ui.keyCode.SPACE &&
252 | that.options.allowSpaces !== true &&
253 | (
254 | $.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
255 | (
256 | $.trim(that.tagInput.val()).charAt(0) == '"' &&
257 | $.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' &&
258 | $.trim(that.tagInput.val()).length - 1 !== 0
259 | )
260 | )
261 | )
262 | ) {
263 | // Enter submits the form if there's no text in the input.
264 | if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) {
265 | event.preventDefault();
266 | }
267 |
268 | // Autocomplete will create its own tag from a selection and close automatically.
269 | if (!that.tagInput.data('autocomplete-open')) {
270 | that.createTag(that._cleanedInput());
271 | }
272 | }
273 | }).blur(function(e){
274 | // Create a tag when the element loses focus.
275 | // If autocomplete is enabled and suggestion was clicked, don't add it.
276 | if (!that.tagInput.data('autocomplete-open')) {
277 | that.createTag(that._cleanedInput());
278 | }
279 | });
280 |
281 | // Autocomplete.
282 | if (this.options.availableTags || this.options.tagSource || this.options.autocomplete.source) {
283 | var autocompleteOptions = {
284 | select: function(event, ui) {
285 | that.createTag(ui.item);
286 | // Preventing the tag input to be updated with the chosen value.
287 | return false;
288 | },
289 | open: function(event, ui) {
290 | that.tagInput.data('autocomplete-open', true);
291 | },
292 | close: function(event, ui) {
293 | that.tagInput.data('autocomplete-open', false);
294 | }
295 | };
296 | $.extend(autocompleteOptions, this.options.autocomplete);
297 |
298 | // tagSource is deprecated, but takes precedence here since autocomplete.source is set by default,
299 | // while tagSource is left null by default.
300 | autocompleteOptions.source = this.options.tagSource || autocompleteOptions.source;
301 |
302 | this.tagInput.autocomplete(autocompleteOptions);
303 | }
304 | },
305 |
306 | _cleanedInput: function() {
307 | // Returns the contents of the tag input, cleaned and ready to be passed to createTag
308 | return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1'));
309 | },
310 |
311 | _lastTag: function() {
312 | return this.tagList.find('.tagit-choice:last:not(.removed)');
313 | },
314 |
315 | _tags: function() {
316 | return this.tagList.find('.tagit-choice:not(.removed)');
317 | },
318 |
319 | assignedTags: function() {
320 | // Returns an array of tag string values
321 | var that = this;
322 | var tags = [];
323 | if (this.options.singleField) {
324 | tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
325 | if (tags[0] === '') {
326 | tags = [];
327 | }
328 | } else {
329 | this._tags().each(function() {
330 | tags.push(that.tagLabel(this));
331 | });
332 | }
333 | return tags;
334 | },
335 |
336 | /**
337 | * @return a list of objects (if available), otherwise this returns the same as assignedTags
338 | */
339 | assignedObjects: function () {
340 | var that = this;
341 | var tags = [];
342 | this._tags().each(function() {
343 | if($(this).data().value)
344 | tags.push($(this).data().value);
345 | else
346 | tags.push(that.tagLabel(this));
347 | });
348 | return tags;
349 | },
350 |
351 | _updateSingleTagsField: function(tags) {
352 | // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
353 | $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change');
354 | },
355 |
356 | _subtractArray: function(a1, a2) {
357 | var result = [];
358 | for (var i = 0; i < a1.length; i++) {
359 | if ($.inArray(a1[i], a2) == -1) {
360 | result.push(a1[i]);
361 | }
362 | }
363 | return result;
364 | },
365 |
366 | tagLabel: function(tag) {
367 | // Returns the tag's string label.
368 | if (this.options.singleField) {
369 | return $(tag).find('.tagit-label:first').text();
370 | } else {
371 | return $(tag).find('input:first').val();
372 | }
373 | },
374 |
375 | _showAutocomplete: function() {
376 | this.tagInput.autocomplete('search', '');
377 | },
378 |
379 | _findTagByLabel: function(name) {
380 | var that = this;
381 | var tag = null;
382 | this._tags().each(function(i) {
383 | if (that._formatStr(name) == that._formatStr(that.tagLabel(this))) {
384 | tag = $(this);
385 | return false;
386 | }
387 | });
388 | return tag;
389 | },
390 |
391 | _isNew: function(name) {
392 | return !this._findTagByLabel(name);
393 | },
394 |
395 | _formatStr: function(str) {
396 | if (this.options.caseSensitive) {
397 | return str;
398 | }
399 | return $.trim(str.toLowerCase());
400 | },
401 |
402 | _effectExists: function(name) {
403 | return Boolean($.effects && ($.effects[name] || ($.effects.effect && $.effects.effect[name])));
404 | },
405 |
406 | /**
407 | * @param value either a string (the tagLabel) or an object: { label: "label", data: "attachdata" }
408 | */
409 | createTag: function(value, additionalClass, duringInitialization) {
410 | var that = this;
411 | var data = null;
412 | // allow value to be an object - take the label then
413 | if($.isPlainObject(value)) {
414 | data = value.data;
415 | if(value.value)
416 | value = value.value;
417 | else
418 | value = value.label;
419 | }
420 |
421 | value = $.trim(value);
422 |
423 | if(this.options.preprocessTag) {
424 | value = this.options.preprocessTag(value, data);
425 | }
426 |
427 | if (value === '' || !value) {
428 | return false;
429 | }
430 |
431 | if (!this.options.allowDuplicates && !this._isNew(value)) {
432 | var existingTag = this._findTagByLabel(value);
433 | if (this._trigger('onTagExists', null, {
434 | existingTag: existingTag,
435 | duringInitialization: duringInitialization
436 | }) !== false) {
437 | if (this._effectExists('highlight')) {
438 | existingTag.effect('highlight');
439 | }
440 | }
441 | return false;
442 | }
443 |
444 | if (this.options.tagLimit && this._tags().length >= this.options.tagLimit) {
445 | this._trigger('onTagLimitExceeded', null, {duringInitialization: duringInitialization});
446 | return false;
447 | }
448 |
449 | var label = $(this.options.onTagClicked ? ' ' : ' ').text(value);
450 |
451 | // Create tag.
452 | var tag = $(' ')
453 | .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
454 | .addClass(additionalClass)
455 | .append(label);
456 |
457 | if(data !== null) {
458 | tag.data().value = data;
459 | }
460 |
461 | if (this.options.readOnly){
462 | tag.addClass('tagit-choice-read-only');
463 | } else {
464 | tag.addClass('tagit-choice-editable');
465 | // Button for removing the tag.
466 | var removeTagIcon = $(' ')
467 | .addClass('ui-icon ui-icon-close');
468 | var removeTag = $('\xd7 ') // \xd7 is an X
469 | .addClass('tagit-close')
470 | .append(removeTagIcon)
471 | .click(function(e) {
472 | // Removes a tag when the little 'x' is clicked.
473 | that.removeTag(tag);
474 | });
475 | tag.append(removeTag);
476 | }
477 |
478 | // Unless options.singleField is set, each tag has a hidden input field inline.
479 | if (!this.options.singleField) {
480 | var escapedValue = label.html();
481 | tag.append(' ');
482 | }
483 |
484 | if (this._trigger('beforeTagAdded', null, {
485 | tag: tag,
486 | tagLabel: this.tagLabel(tag),
487 | duringInitialization: duringInitialization
488 | }) === false) {
489 | return;
490 | }
491 |
492 | if (this.options.singleField) {
493 | var tags = this.assignedTags();
494 | tags.push(value);
495 | this._updateSingleTagsField(tags);
496 | }
497 |
498 | // DEPRECATED.
499 | this._trigger('onTagAdded', null, tag);
500 |
501 | this.tagInput.val('');
502 |
503 | // Insert tag.
504 | this.tagInput.parent().before(tag);
505 |
506 | this._trigger('afterTagAdded', null, {
507 | tag: tag,
508 | tagLabel: this.tagLabel(tag),
509 | duringInitialization: duringInitialization
510 | });
511 |
512 | this.element.trigger("change");
513 |
514 | if (this.options.showAutocompleteOnFocus && !duringInitialization) {
515 | setTimeout(function () { that._showAutocomplete(); }, 0);
516 | }
517 | },
518 |
519 | removeTag: function(tag, animate) {
520 | animate = typeof animate === 'undefined' ? this.options.animate : animate;
521 |
522 | tag = $(tag);
523 |
524 | // DEPRECATED.
525 | this._trigger('onTagRemoved', null, tag);
526 |
527 | if (this._trigger('beforeTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)}) === false) {
528 | return;
529 | }
530 |
531 | if (this.options.singleField) {
532 | var tags = this.assignedTags();
533 | var removedTagLabel = this.tagLabel(tag);
534 | tags = $.grep(tags, function(el){
535 | return el != removedTagLabel;
536 | });
537 | this._updateSingleTagsField(tags);
538 | }
539 |
540 | if (animate) {
541 | tag.addClass('removed'); // Excludes this tag from _tags.
542 | var hide_args = this._effectExists('blind') ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast'];
543 |
544 | var thisTag = this;
545 | hide_args.push(function() {
546 | tag.remove();
547 | thisTag._trigger('afterTagRemoved', null, {tag: tag, tagLabel: thisTag.tagLabel(tag)});
548 | this.element.trigger("change");
549 | });
550 |
551 | tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue();
552 | } else {
553 | tag.remove();
554 | this._trigger('afterTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)});
555 | this.element.trigger("change");
556 | }
557 |
558 | },
559 |
560 | removeTagByLabel: function(tagLabel, animate) {
561 | var toRemove = this._findTagByLabel(tagLabel);
562 | if (!toRemove) {
563 | throw "No such tag exists with the name '" + tagLabel + "'";
564 | }
565 | this.removeTag(toRemove, animate);
566 | },
567 |
568 | removeAll: function() {
569 | // Removes all tags.
570 | var that = this;
571 | this._tags().each(function(index, tag) {
572 | that.removeTag(tag, false);
573 | });
574 | }
575 |
576 | });
577 | })(jQuery);
578 |
579 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery.jsForm",
3 | "title": "jQuery JSON Form - jsForm",
4 | "description": "jQuery based form library that allows you to handle data within a js object (i.e. JSON) with html forms.\nYou can modify all fields/properties by simply creating a html form with the correct naming schema (MVVM Pattern).\n\nThe main features of this library are:\n Full standard html with data available in a js object\n Update an existing js object with changes done within a form\n Fully internationalizable with number format, currency and date formatting\n Provide basic functions for formatting (i.e. date/time, money) using html markup\n Provide form validation functionality\n handle collections (arrays) with subobjects\n provides helper methods to handle array manipulation (add new entry/remove an entry, sorting) using only html markup\n Can be used in connection with an autocomplete function to add new array objects",
5 | "version": "1.6.0",
6 | "homepage": "https://github.com/corinis/jsForm",
7 | "author": {
8 | "name": "Niko Berger",
9 | "url": "http://www.gargan.org/"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/corinis/jsForm.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/corinis/jsForm/issues"
17 | },
18 | "licenses": [
19 | {
20 | "type": "MIT, GPL, LGPL",
21 | "url": "https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt"
22 | }
23 | ],
24 | "dependencies": {
25 | "grunt": "^1.4.1",
26 | "grunt-compare-size": "^0.4.2",
27 | "grunt-contrib-concat": "^1.0.1",
28 | "grunt-contrib-jshint": "^3.2.0",
29 | "grunt-contrib-uglify": "^5.0.1",
30 | "grunt-contrib-watch": "^1.1.0",
31 | "jshint": "^2.13.6"
32 | },
33 | "keywords": []
34 | }
35 |
--------------------------------------------------------------------------------
/sample-conditional.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
34 |
38 |
39 |
40 | Conditional Form Test
41 | This form is all about sho/hiding parts of the dom based on data and input
42 |
43 | Name:
44 |
activate Age
45 |
46 | Age:
47 |
48 |
49 |
50 | visible
51 | important
52 | hidden
53 |
54 |
55 | links are only shown if name or active is set
56 | Links
57 |
60 |
61 |
add a link
62 | Additional field:
63 |
Fill something in the addional field and apply conditionals to see quips
64 |
65 | Quips
66 |
69 | add a quip
70 |
71 |
72 | Show Object Apply Conditionals
73 |
74 |
--------------------------------------------------------------------------------
/sample-connect.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
31 |
32 |
33 | Connect two Forms into one
34 | In this example, the form is split into two dom elements, with no direct connection, but they are filled with one jsForm call and also put together correctly.
35 |
54 |
55 | The second part
56 |
57 |
58 |
59 | Quips
60 |
63 | add a quip
64 |
65 |
66 |
67 | Show Object
68 |
69 |
--------------------------------------------------------------------------------
/sample-dataHandler.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
49 |
50 |
51 | Simple Form Test
52 |
53 | Name:
54 |
55 |
56 | Links
57 |
60 |
61 |
add a link
62 | Additional field:
63 |
64 |
65 | Show Object
66 |
67 |
--------------------------------------------------------------------------------
/sample-multiselect.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
48 |
49 |
50 | Multi Select test
51 |
52 | Name:
53 |
54 |
58 |
59 |
60 |
67 |
68 |
69 | Click to select:
70 |
71 | User 1
72 | User 2
73 | User 3
74 | User 4
75 | User 5
76 |
77 |
78 |
79 | Show Object
80 |
81 |
--------------------------------------------------------------------------------
/sample-object.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
76 |
77 |
78 |
79 | Simple Form Test - working with objects
80 |
81 |
82 |
96 |
97 |
98 |
99 |
groups.name
100 | props:
+
101 |
102 |
103 | =props.value -
104 |
105 |
106 |
107 |
108 |
109 | Show Object
110 |
111 |
--------------------------------------------------------------------------------
/sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
33 |
34 |
35 | Simple Form Test
36 |
75 | Show Object
76 |
77 |
--------------------------------------------------------------------------------
/sortable-editable-sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
47 |
48 |
49 | Multi level array testform
50 |
51 | Name:
52 |
active
53 |
54 | visible
55 | important
56 | hidden
57 |
58 |
69 |
70 |
71 |
72 | Show Object
73 |
74 |
--------------------------------------------------------------------------------
/sortable-sample.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
47 |
48 |
49 | Multi level array testform
50 |
51 | Name:
52 |
active
53 |
54 | visible
55 | important
56 | hidden
57 |
58 |
59 |
60 | Group: groups.name
61 |
64 | up down
65 |
66 |
67 |
68 | Show Object
69 |
70 |
--------------------------------------------------------------------------------
/src/jquery.jsForm.d.ts:
--------------------------------------------------------------------------------
1 | interface JQuery {
2 | /**
3 | * Initialize jsform with default configuration on a node
4 | */
5 | jsForm(): JQuery;
6 | jsForm(func:any): JQuery;
7 | jsForm(func:string, opts:any): JQuery;
8 | }
--------------------------------------------------------------------------------
/test/jquery.jsForm.performance.js:
--------------------------------------------------------------------------------
1 | module('jquery.jsForm.js');
2 |
3 |
4 | test("predefined form", function(){
5 | // html code for basic form
6 | var basicForm = $('\n'+
7 | '
\n'+
8 | '
\n'+
9 | '
\n'+
10 | '
\n'+
11 | '
\n'+
12 | '
\n'+
21 | '
\n'+
22 | '
\n'+
23 | ' \n'+
24 | ' \n'+
25 | ' \n'+
26 | ' \n'+
27 | ' \n'+
28 | '
'+
29 | '
\n'+
30 | '
\n'+
31 | '
\n'+
32 | ' none val1 val2 val3 \n'+
33 | ' none val1 val2 val3 \n'+
34 | ' none val1 val2 val3 \n'+
35 | ' none val1 val2 val3 \n'+
36 | ' none val1 val2 val3 \n'+
37 | '
'+
38 | '
\n'+
39 | '
');
40 |
41 | $("body").append(basicForm);
42 | var REPEAT = 20;
43 |
44 | var data = {groups1:[], groups2:[], groups3:[]};
45 | // basedata
46 | for(var i = 1; i <= 5; i++ ) {
47 | data["input" + i] = "LOREM IPSUM " + i;
48 | }
49 | //collection
50 | for(var j = 0; j < REPEAT; j++ ) {
51 | var obj = {};
52 | for(var i = 1; i <= 5; i++ )
53 | obj["input" + i] = "LOREM IPSUM " + i;
54 |
55 | data.groups1.push(obj);
56 | }
57 | //collection
58 | for(var j = 0; j < REPEAT; j++ ) {
59 | var obj = {};
60 | for(var i = 1; i <= 5; i++ )
61 | obj["input" + i] = "LOREM IPSUM DOLOR " + i;
62 | data.groups2.push(obj);
63 | }
64 | //collection
65 | for(var j = 0; j < REPEAT; j++ ) {
66 | var obj = {};
67 | for(var i = 1; i <= 5; i++ )
68 | obj["input" + i] = "val2";
69 | data.groups3.push(obj);
70 | }
71 |
72 | var start = new Date().getTime();
73 |
74 | // default init: prefix = data
75 | basicForm.jsForm({
76 | data: data
77 | });
78 |
79 | test("time for filling", function(){
80 | ok(true, (new Date().getTime() - start) + "ms");
81 | });
82 |
83 |
84 | equal(basicForm.jsForm("equals", data), true, "equals");
85 |
86 | var RUNS = 5;
87 | start = new Date().getTime();
88 |
89 | for(var i = 0; i < RUNS; i++) {
90 | basicForm.jsForm("get");
91 | }
92 |
93 | test("time for getting", function(){
94 | ok(true, (new Date().getTime() - start)/RUNS + "ms");
95 | });
96 |
97 |
98 | start = new Date().getTime();
99 |
100 | for(var i = 0; i < RUNS; i++) {
101 | basicForm.jsForm("changed");
102 | }
103 |
104 | test("time for changed", function(){
105 | ok(true, (new Date().getTime() - start)/RUNS + "ms");
106 | });
107 |
108 | basicForm.remove();
109 | });
110 |
111 |
--------------------------------------------------------------------------------
/test/jquery.jsForm.test.js:
--------------------------------------------------------------------------------
1 | module('jquery.jsForm.js');
2 |
3 | test("data formatting", function(){
4 | equal($.jsFormControls.Format.humanTime(320), "320ms", "milliseconds");
5 | equal($.jsFormControls.Format.humanTime(6000), "6s", "milliseconds");
6 | equal($.jsFormControls.Format.humanTime(4320), "4s", "seconds");
7 | equal($.jsFormControls.Format.humanTime(184200), "3m 4s", "minutest and seconds");
8 | equal($.jsFormControls.Format.humanTime(5*3600000 + 32*60000 + 3580), "5h 32m", "hours and minutes");
9 | equal($.jsFormControls.Format.decimal(51234.1234), "51,234.12", "decimal");
10 | equal($.jsFormControls.Format.decimal(4.1234), "4.12", "decimal");
11 | });
12 |
13 | test("internationalization", function(){
14 | // default: english
15 | equal($.jsFormControls.Format.decimal(1123456.54), "1,123,456.54", "default decimal");
16 | //$.jsFormControls.Format.dateTime(cdata)
17 | //$.jsFormControls.Format.date(cdata)
18 | equal($.jsFormControls.Format.asNumber("1,123,456.54"), 1123456.54, "decimal to number");
19 |
20 | // set english locale
21 | $(document).data("i18n", {
22 | date: {
23 | "format": "M/d/yy h:mm a",
24 | "monthsFull":["January","February","March","April","May","June","July","August","September","October","November","December"],
25 | "monthsShort": ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
26 | "daysFull": ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
27 | "daysShort": ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],
28 | "timeFormat": "h:mm a",
29 | "longDateFormat": "EEEE, MMMM d, yyyy",
30 | "shortDateFormat": "M/d/yy"
31 | },
32 | number: {
33 | "format": "#,##0.###",
34 | "groupingSeparator": ".",
35 | "decimalSeparator": ","
36 | },
37 | currency: {
38 | "prefix": "",
39 | "suffix": " €",
40 | "fractionDigits": 2
41 | }
42 | });
43 |
44 |
45 | equal($.jsFormControls.Format.decimal(1123456.54), "1.123.456,54", "decimal (de)");
46 | //$.jsFormControls.Format.dateTime(cdata)
47 | //$.jsFormControls.Format.date(cdata)
48 | equal($.jsFormControls.Format.currency(1123456.54), "1.123.456,54 €", "currency (€ suffix)");
49 | equal($.jsFormControls.Format.asNumber("1.123.456,54 €"), 1123456.54, "currency number (€ suffix)");
50 |
51 | // set german locale
52 | $(document).data("i18n", {
53 | date: {
54 | "format": "dd.MM.yy HH:mm",
55 | "monthsFull":["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],
56 | "monthsShort": ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],
57 | "daysFull": ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],
58 | "daysShort": ["So","Mo","Di","Mi","Do","Fr","Sa"],
59 | "timeFormat": "HH:mm",
60 | "longDateFormat": "EEEE, d. MMMM yyyy",
61 | "shortDateFormat": "dd.MM.yy"
62 | },
63 | number: {
64 | "format": "#,##0.###",
65 | "groupingSeparator": ",",
66 | "decimalSeparator": "."
67 | },
68 | currency: {
69 | "prefix": "$",
70 | "suffix": "",
71 | "fractionDigits": 2
72 | }
73 | });
74 | equal($.jsFormControls.Format.decimal(1123456.54), "1,123,456.54", "decimal US");
75 | //$.jsFormControls.Format.dateTime(cdata)
76 | //$.jsFormControls.Format.date(cdata)
77 | equal($.jsFormControls.Format.currency(1123456.54), "$1,123,456.54", "currency ($ prefix)");
78 | equal($.jsFormControls.Format.asNumber("$1,123,456.54"), 1123456.54, "currency number ($ prefix)");
79 |
80 | });
81 |
82 | test("slickgrid parameter formatting", function(){
83 | equal($.jsFormControls.Format.humanTime(10, 10, 320), "320ms", "human time");
84 | });
85 |
86 |
87 | test("basic form", function(){
88 | // html code for basic form
89 | var basicForm = $('\n'+
90 | ' \n'+
91 | '\n'+
92 | ' opt1 \n'+
93 | ' option2 \n'+
94 | ' opt3 \n'+
95 | ' \n'+
96 | ' \n'+
97 | '\n'+
98 | ' \n'+
99 | 'data.field \n'+
100 | '
');
101 |
102 | $("body").append(basicForm);
103 |
104 | var original = {
105 | input: "inputTest",
106 | checkbox: true,
107 | select: "opt2",
108 | textarea: "ta\ntest",
109 | field: 123456
110 | };
111 |
112 | // default init: prefix = data
113 | basicForm.jsForm({
114 | data: original
115 | });
116 |
117 | equal($("input[name='data.input']", basicForm).val(), "inputTest", "input");
118 | equal($("input[name='data.checkbox']", basicForm).is(":checked"), true, "checkbox");
119 | equal($("select[name='data.select']", basicForm).val(), "opt2", "select");
120 | equal($("textarea[name='data.textarea']", basicForm).val(), "ta\ntest", "textarea");
121 | equal($("input[name='input']", basicForm).val(), "nochange", "ignore non-namespace input");
122 | equal($("#simpleFormField", basicForm).html(), "123456", "field");
123 |
124 | // make sure we get the same data out of the form
125 | equal(basicForm.jsForm("equals", original), true, "form hasn't changed the data");
126 |
127 | // update the fields
128 | $("input[name='data.input']", basicForm).val("input2");
129 | $("input[name='data.checkbox']", basicForm).click();
130 | $("select[name='data.select']", basicForm).find('option[value="opt3"]').prop('selected', true);
131 | $("textarea[name='data.textarea']", basicForm).val("test\n2");
132 | $("input[name='input']", basicForm).val("CHANGED");
133 | $("#simpleFormField", basicForm).html("ASDF");
134 |
135 | // compare the form with the original data
136 | equal(basicForm.jsForm("equals", original), false, "form has different data");
137 |
138 | // get the pojo
139 | var pojo = basicForm.jsForm("get");
140 |
141 |
142 | // check the pojo with the updated values
143 | equal(pojo.input,"input2", "input (updated)");
144 | equal(pojo.checkbox, false, "checkbox (updated)");
145 | equal(pojo.select, "opt3", "select (updated)");
146 | equal(pojo.textarea, "test\n2", "textarea (updated)");
147 | equal(pojo.field, 123456, "field (no change) (updated)");
148 |
149 | // cleanup
150 | basicForm.remove();
151 | });
152 |
153 |
154 | test("collection test", function(){
155 | // html code for basic form
156 | var basicForm = $('\n'+
157 | '
\n'+
158 | '
\n'+
159 | '
'+
160 | 'groups.id \n'+
161 | ' \n'+
162 | ' X \n'+
163 | '
'+
164 | '
\n'+
165 | '
N \n'+
166 | '
\n'+
167 | '
\n'+
168 | '
');
169 |
170 | $("body").append(basicForm);
171 |
172 | var original = {
173 | input: "inputTest",
174 | test: "testValue",
175 | groups: [
176 | { name: "group1", id: 1 },
177 | { name: "group2", id: 2 },
178 | { name: "group3", id: 3 }
179 | ],
180 | simpleString: ["test", "test2", "test3"],
181 | simpleNumber: [1, 2, 3]
182 | };
183 |
184 | // default init: prefix = data
185 | basicForm.jsForm({
186 | data: original
187 | });
188 |
189 | equal($("input[name='data.input']", basicForm).val(), "inputTest", "simple input");
190 | equal($("div.collection.group", basicForm).children().size(), 3, "3 repeats");
191 | equal($("div.collection.number", basicForm).children().size(), 3, "3 repeats");
192 | equal($("div.collection.string", basicForm).children().size(), 3, "3 repeats");
193 |
194 |
195 | equal(basicForm.jsForm("equals", original), true, "form has not changed");
196 |
197 | // update a field
198 | $("div.collection.group div", basicForm).eq(1).find("input").val("TEST2");
199 | $("div.collection.string div", basicForm).eq(1).find("input").val("HELLO WORLD");
200 | $("div.collection.number div", basicForm).eq(1).find("input").val("1000");
201 |
202 | equal(basicForm.jsForm("equals", original), false, "form has different data");
203 |
204 | // get object back and test
205 | var pojo = basicForm.jsForm("get");
206 |
207 | equal(pojo.groups[1].name, "TEST2", "test change in collection");
208 | equal(pojo.simpleString[1], "HELLO WORLD", "test change in collection");
209 | equal(pojo.simpleNumber[1] === 1000, true, "test change in collection");
210 |
211 | // add a new entry using "simple controls"
212 | $("span.add", basicForm).click();
213 | equal($("div.collection.group", basicForm).children().size(), 4, "4 repeats");
214 |
215 | pojo = basicForm.jsForm("get");
216 | equal(pojo.groups.length, 3, "no data yet - 3 entries");
217 |
218 | // enter some data
219 | $("div.collection.group div", basicForm).eq(3).find("input").val("newTest");
220 |
221 | pojo = basicForm.jsForm("get");
222 | equal(pojo.groups.length, 4, "with data - 4 entries");
223 |
224 | // remove
225 | $("div.collection.group div", basicForm).eq(1).find("span.delete").click();
226 | equal($("div.collection.group", basicForm).children().size(), 3, "3 repeats");
227 | equal($("div.collection.group div", basicForm).eq(1).find("input").val(), "group3", "check if correct field is removed");
228 | pojo = basicForm.jsForm("get");
229 | equal(pojo.groups.length, 3, "remove entry");
230 |
231 | basicForm.remove();
232 | });
233 |
234 |
235 | test("collection in collection test", function(){
236 | // html code for basic form
237 | var basicForm = $('\n'+
238 | '
\n'+
239 | '
\n'+
240 | '
\n'+
241 | '
\n'+
242 | '
'+
249 | '
'+
250 | '
\n'+
251 | '
N \n'+
252 | '
');
253 |
254 | $("body").append(basicForm);
255 |
256 | var original = {
257 | input: "inputTest",
258 | groups: [
259 | { name: "group1", users: [{name: "user11"}, {name: "user12"}] },
260 | { name: "group2"},
261 | { name: "group3", users: [{name: "user31"}, {name: "user32"}] }
262 | ]
263 | };
264 |
265 | // default init: prefix = data
266 | basicForm.jsForm({
267 | data: original
268 | });
269 |
270 | equal($("div.collection.group", basicForm).children().size(), 3, "3 repeats");
271 | equal($("div.collection.group", basicForm).children().eq(0).find(".collection.users").children(".entry").size(), 2, "2 repeats (child)");
272 |
273 | equal(basicForm.jsForm("equals", original), true, "form has not changed");
274 |
275 | // update a field
276 | $("div.collection.group", basicForm).children().eq(0).find(".collection.users").children().eq(0).find("input[name='users.name']").val("TESTUSER");
277 | $("div.collection.group", basicForm).children().eq(0).find(".collection.users").children().eq(1).find("input[name='users.value']").val("33");
278 |
279 | equal(basicForm.jsForm("equals", original), false, "form has different data");
280 |
281 | // get object back and test
282 | var pojo = basicForm.jsForm("get");
283 |
284 | equal(pojo.groups[0].users[0].name, "TESTUSER", "test change in collection");
285 | equal(pojo.groups[0].users[1].value, 33, "test change in collection");
286 |
287 | basicForm.remove();
288 | });
289 |
290 | test("simple arrays and checkboxes", function(){
291 | // html code for basic form
292 | var basicForm = $('\n'+
293 | ' \n'+
294 | ' \n'+
295 | ' \n'+
296 | ' \n'+
297 | '
');
298 |
299 | $("body").append(basicForm);
300 |
301 | var original = {
302 | checks: ["a", "c"]
303 | };
304 |
305 | // default init: prefix = data
306 | basicForm.jsForm({
307 | data: original
308 | });
309 |
310 | equal($("input.array[value='a']", basicForm).is(":checked"), true, "a checked");
311 | equal($("input.array[value='b']", basicForm).is(":checked"), false, "b not checked");
312 | equal($("input.array[value='c']", basicForm).is(":checked"), true, "c checked");
313 | equal($("input.array[value='d']", basicForm).is(":checked"), false, "d not checked");
314 | equal(basicForm.jsForm("equals", original), true, "form has not changed");
315 | /*
316 | // update a field
317 | $("input.array[value='a']", basicForm).prop("checked", false);
318 | $("input.array[value='b']", basicForm).prop("checked", true);
319 |
320 | equal(basicForm.jsForm("equals", original), false, "form has different data");
321 |
322 | // get object back and test
323 | var pojo = basicForm.jsForm("get");
324 |
325 | equal(pojo.checks, ["b", "c"], "b and c checked");
326 | */
327 | basicForm.remove();
328 |
329 | });
330 |
331 |
332 | test("direct access simple arrays", function(){
333 | // html code for basic form
334 | var basicForm = $('\n'+
335 | '
\b'+
338 | '
add \n'+
339 | '
\b'+
342 | '
add \n'+
343 | '
');
344 |
345 | $("body").append(basicForm);
346 |
347 | var original = {
348 | steps: [10,20,30,40,50],
349 | test: {
350 | steps: [100,200,300,400,500]
351 | }
352 | };
353 |
354 | // default init: prefix = data
355 | basicForm.jsForm({
356 | data: original
357 | });
358 |
359 | equal($("#list1", basicForm).children().length, 5, "5 items in list1");
360 | equal($("#list2", basicForm).children().length, 5, "5 items in list2");
361 | equal(basicForm.jsForm("changed"), false, "form has not changed");
362 | equal(basicForm.jsForm("equals", original), true, "form data has not changed");
363 | // add an item in each list
364 | $("#add1", basicForm).click();
365 | $("#add2", basicForm).click();
366 | equal($("#list1", basicForm).children().length, 6, "6 items in list1");
367 | equal($("#list2", basicForm).children().length, 6, "6 items in list2");
368 |
369 | $("#list1", basicForm).children().first().find("input").val("1111").change();
370 | $("#list1", basicForm).children().last().find("input").val("1112").change();
371 | $("#list2", basicForm).children().first().find("input").val("2221").change();
372 | $("#list2", basicForm).children().last().find("input").val("2222").change();
373 | var updated = basicForm.jsForm("get");
374 | equal(updated.steps.length, 6, "6 steps");
375 | equal(updated.steps[0], 1111, "first element: 1111");
376 | equal(updated.steps[5], 1112, "last element: 1112");
377 | equal(updated.test.steps.length, 6, "6 steps in test");
378 | equal(updated.test.steps[0], 2221, "first element: 2221");
379 | equal(updated.test.steps[5], 2222, "last element: 2222");
380 |
381 | equal($("#list1", basicForm).children().first().find("input").hasClass("changed"), true, "first input changed");
382 | equal($("#list1", basicForm).children().eq(2).find("input").hasClass("changed"), false, "second input not changed");
383 | equal($("#list1", basicForm).children().last().find("input").hasClass("changed"), true, "last input changed");
384 | equal($("#list2", basicForm).children().first().find("input").hasClass("changed"), true, "first input changed");
385 | equal($("#list2", basicForm).children().last().find("input").hasClass("changed"), true, "last input changed");
386 |
387 | equal(basicForm.jsForm("changed"), true, "form has changed");
388 | equal(basicForm.jsForm("equals", original), false, "form data has changed");
389 | // clean up
390 | basicForm.remove();
391 | });
--------------------------------------------------------------------------------
/test/performance.jquery.jsForm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | performance.jquery.jsForm.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Status Window
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/test/qunit-1.10.0.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.10.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://qunitjs.com
5 | *
6 | * Copyright 2012 jQuery Foundation and other contributors
7 | * Released under the MIT license.
8 | * http://jquery.org/license
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 5px 5px 0 0;
42 | -moz-border-radius: 5px 5px 0 0;
43 | -webkit-border-top-right-radius: 5px;
44 | -webkit-border-top-left-radius: 5px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-testrunner-toolbar label {
58 | display: inline-block;
59 | padding: 0 .5em 0 .1em;
60 | }
61 |
62 | #qunit-banner {
63 | height: 5px;
64 | }
65 |
66 | #qunit-testrunner-toolbar {
67 | padding: 0.5em 0 0.5em 2em;
68 | color: #5E740B;
69 | background-color: #eee;
70 | overflow: hidden;
71 | }
72 |
73 | #qunit-userAgent {
74 | padding: 0.5em 0 0.5em 2.5em;
75 | background-color: #2b81af;
76 | color: #fff;
77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
78 | }
79 |
80 | #qunit-modulefilter-container {
81 | float: right;
82 | }
83 |
84 | /** Tests: Pass/Fail */
85 |
86 | #qunit-tests {
87 | list-style-position: inside;
88 | }
89 |
90 | #qunit-tests li {
91 | padding: 0.4em 0.5em 0.4em 2.5em;
92 | border-bottom: 1px solid #fff;
93 | list-style-position: inside;
94 | }
95 |
96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
97 | display: none;
98 | }
99 |
100 | #qunit-tests li strong {
101 | cursor: pointer;
102 | }
103 |
104 | #qunit-tests li a {
105 | padding: 0.5em;
106 | color: #c2ccd1;
107 | text-decoration: none;
108 | }
109 | #qunit-tests li a:hover,
110 | #qunit-tests li a:focus {
111 | color: #000;
112 | }
113 |
114 | #qunit-tests ol {
115 | margin-top: 0.5em;
116 | padding: 0.5em;
117 |
118 | background-color: #fff;
119 |
120 | border-radius: 5px;
121 | -moz-border-radius: 5px;
122 | -webkit-border-radius: 5px;
123 | }
124 |
125 | #qunit-tests table {
126 | border-collapse: collapse;
127 | margin-top: .2em;
128 | }
129 |
130 | #qunit-tests th {
131 | text-align: right;
132 | vertical-align: top;
133 | padding: 0 .5em 0 0;
134 | }
135 |
136 | #qunit-tests td {
137 | vertical-align: top;
138 | }
139 |
140 | #qunit-tests pre {
141 | margin: 0;
142 | white-space: pre-wrap;
143 | word-wrap: break-word;
144 | }
145 |
146 | #qunit-tests del {
147 | background-color: #e0f2be;
148 | color: #374e0c;
149 | text-decoration: none;
150 | }
151 |
152 | #qunit-tests ins {
153 | background-color: #ffcaca;
154 | color: #500;
155 | text-decoration: none;
156 | }
157 |
158 | /*** Test Counts */
159 |
160 | #qunit-tests b.counts { color: black; }
161 | #qunit-tests b.passed { color: #5E740B; }
162 | #qunit-tests b.failed { color: #710909; }
163 |
164 | #qunit-tests li li {
165 | padding: 5px;
166 | background-color: #fff;
167 | border-bottom: none;
168 | list-style-position: inside;
169 | }
170 |
171 | /*** Passing Styles */
172 |
173 | #qunit-tests li li.pass {
174 | color: #3c510c;
175 | background-color: #fff;
176 | border-left: 10px solid #C6E746;
177 | }
178 |
179 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
180 | #qunit-tests .pass .test-name { color: #366097; }
181 |
182 | #qunit-tests .pass .test-actual,
183 | #qunit-tests .pass .test-expected { color: #999999; }
184 |
185 | #qunit-banner.qunit-pass { background-color: #C6E746; }
186 |
187 | /*** Failing Styles */
188 |
189 | #qunit-tests li li.fail {
190 | color: #710909;
191 | background-color: #fff;
192 | border-left: 10px solid #EE5757;
193 | white-space: pre;
194 | }
195 |
196 | #qunit-tests > li:last-child {
197 | border-radius: 0 0 5px 5px;
198 | -moz-border-radius: 0 0 5px 5px;
199 | -webkit-border-bottom-right-radius: 5px;
200 | -webkit-border-bottom-left-radius: 5px;
201 | }
202 |
203 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
204 | #qunit-tests .fail .test-name,
205 | #qunit-tests .fail .module-name { color: #000000; }
206 |
207 | #qunit-tests .fail .test-actual { color: #EE5757; }
208 | #qunit-tests .fail .test-expected { color: green; }
209 |
210 | #qunit-banner.qunit-fail { background-color: #EE5757; }
211 |
212 |
213 | /** Result */
214 |
215 | #qunit-testresult {
216 | padding: 0.5em 0.5em 0.5em 2.5em;
217 |
218 | color: #2b81af;
219 | background-color: #D2E0E6;
220 |
221 | border-bottom: 1px solid white;
222 | }
223 | #qunit-testresult .module-name {
224 | font-weight: bold;
225 | }
226 |
227 | /** Fixture */
228 |
229 | #qunit-fixture {
230 | position: absolute;
231 | top: -10000px;
232 | left: -10000px;
233 | width: 1000px;
234 | height: 1000px;
235 | }
236 |
--------------------------------------------------------------------------------
/test/test.jquery.jsForm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | jquery.jsForm.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Status Window
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------