items */
38 | .owl-tagbox li {
39 | display: inline-block;
40 | float: left;
41 | font-size: 14px;
42 | line-height: 1.428571429;
43 | margin: 6px 6px 0 0;
44 | }
45 |
46 | /* These are the actual tags */
47 | .owl-tagbox li[data-control="tag"] {
48 | background-color: #405D7A;
49 | border-radius: 2px;
50 | border: 1px solid #000;
51 | color: #dedede;
52 | cursor: default;
53 | padding: 3px 8px;
54 | }
55 | .owl-tagbox .tagbox-tag span.value { float: left; }
56 | .owl-tagbox .tagbox-tag span.remove-span { float: right; }
57 |
58 | /* Link for removing a tag */
59 | .owl-tagbox li[data-control="tag"] [data-control="remove"] {
60 | color: inherit;
61 | cursor: pointer;
62 | margin-left: 10px;
63 | text-decoration: none;
64 | }
65 | .owl-tagbox li[data-control="tag"] [data-control="remove"]:hover {
66 | color: #fff;
67 | text-decoration: none;
68 | }
69 |
70 | /* This class will add to pending backspace deletes */
71 | .owl-tagbox li[data-control="tag"].pre-delete {
72 | background-color: #E0444B;
73 | }
74 |
75 | /* This class will be flashed to display duplicates */
76 | .owl-tagbox li[data-control="tag"].flash {
77 | background-color: #457A40 !important;
78 | }
--------------------------------------------------------------------------------
/src/FormWidgets/Money/Widget.php:
--------------------------------------------------------------------------------
1 | prepareVars();
16 | return $this->makePartial('widget');
17 | }
18 |
19 | /**
20 | * Load widget assets
21 | */
22 | public function loadAssets()
23 | {
24 | $this->addJs('js/jquery.maskMoney.js');
25 | }
26 |
27 | /**
28 | * Prepare widget variables
29 | */
30 | public function prepareVars()
31 | {
32 | $this->vars['loadValue'] = $this->getLoadValue();
33 |
34 | $this->vars['config']['placeholder'] = isset($this->config->placeholder)
35 | ? $this->config->placeholder
36 | : '0.00';
37 |
38 | $this->vars['config']['thousands'] = isset($this->config->thousands)
39 | ? $this->config->thousands
40 | : ',';
41 |
42 | $this->vars['config']['decimal'] = isset($this->config->decimal)
43 | ? $this->config->decimal
44 | : '.';
45 |
46 | $this->vars['config']['suffix'] = isset($this->config->suffix)
47 | ? $this->config->suffix
48 | : '';
49 |
50 | $this->vars['config']['prefix'] = isset($this->config->prefix)
51 | ? $this->config->prefix
52 | : '';
53 |
54 | $this->vars['config']['allowNegative'] = isset($this->config->allowNegative) && $this->config->allowNegative
55 | ? 'true'
56 | : 'false';
57 |
58 | // Allow plugins to override the configuration
59 | if ($config = Config::get('owl.formwidgets::money')) {
60 | if (is_array($config) && count($config) >= 1) {
61 | foreach ($config as $key => $value)
62 | $this->vars['config'][$key] = $value;
63 | }
64 | }
65 | }
66 |
67 | /**
68 | * Return save value
69 | *
70 | * @return float
71 | */
72 | public function getSaveValue($value)
73 | {
74 | if (!$input = input($this->fieldName))
75 | return 0;
76 |
77 | $input = preg_replace("/[^0-9]/", '', $input);
78 | $input = substr($input, 0, -2) . '.' . substr($input, -2);
79 | $input = floatval($input);
80 |
81 | return $input;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/FormWidgets/Knob/README.md:
--------------------------------------------------------------------------------
1 | # Knob
2 | A touch friendly widget to handle numeric fields.
3 |
4 | [jQuery Knob](https://github.com/aterrien/jQuery-Knob) is © 2012 Anthony Terrien under the MIT license. OctoberCMS widget created by [Keios Solutions](http://keios.eu).
5 |
6 | 
7 |
8 | ### Installation
9 | To install the Knob widget, add the following to your plugin's ```composer.json``` file.
10 |
11 | ```json
12 | "require": {
13 | "owl/knob": "~1.0@dev"
14 | }
15 | ```
16 | Next, register the widget in your plugin's ```Plugin.php``` file.
17 | ```php
18 | public function registerFormWidgets()
19 | {
20 | return [
21 | 'Owl\FormWidgets\Knob\Widget' => [
22 | 'label' => 'Knob',
23 | 'code' => 'owl-knob'
24 | ],
25 | ];
26 | }
27 | ```
28 |
29 | ### Usage
30 | To use the Knob widget, simply declare a field type as ```owl-knob```
31 | ```yaml
32 | knob:
33 | label: Knob
34 | type: owl-knob
35 | ```
36 | The following options are supported :
37 |
38 | **Behaviors:**
39 | ```
40 | min : min value | default=0.
41 | max : max value | default=100.
42 | step : step size | default=1.
43 | angleOffset : starting angle in degrees | default=0.
44 | angleArc : arc size in degrees | default=360.
45 | stopper : stop at min & max on keydown/mousewheel | default=true.
46 | readOnly : disable input and events | default=false.
47 | rotation : direction of progression | default=clockwise.
48 | ```
49 |
50 | **UI:**
51 | ```
52 | cursor : display mode "cursor", cursor size could be changed passing a numeric value to the option, default width is used when passing boolean value "true" | default=gauge.
53 | thickness : gauge thickness.
54 | lineCap : gauge stroke endings. | default=butt, round=rounded line endings
55 | width : dial width.
56 | displayInput : default=true | false=hide input.
57 | displayPrevious : default=false | true=displays the previous value with transparency.
58 | fgColor : foreground color.
59 | inputColor : input value (number) color.
60 | font : font family.
61 | fontWeight : font weight.
62 | bgColor : background color.
63 | ```
64 |
65 | **October UI related:**
66 | ```
67 | knobLabel: label to display on right side of the knob, can be translation string
68 | knobComment: comment to display on right side of the knob, can be translation string
69 | ```
70 |
71 | **Form configuration example**
72 |
73 | ```yaml
74 | fields:
75 | age:
76 | knobLabel: Your current age
77 | knobComment: We need to know, really.
78 | type: owl-knob
79 | span: right
80 | min: 1
81 | default: 23
82 | max: 100
83 | angleOffset: -125
84 | angleArc: 250
85 | ```
86 |
--------------------------------------------------------------------------------
/src/Widgets/TreeSort/widget/assets/js/treesort.js:
--------------------------------------------------------------------------------
1 | +function ($) { "use strict";
2 |
3 | //
4 | // TreeSort
5 | //
6 | var TreeSort = function ($el) {
7 | var self = this;
8 |
9 | this.$el = $el;
10 |
11 | this.$el.unbind().on('click', function() {
12 | self.popup();
13 | });
14 | }
15 |
16 | //
17 | // Popup form
18 | //
19 | TreeSort.prototype.popup = function() {
20 |
21 | this.$el.on('show.oc.popup', function(e) {
22 | var $popup = $(e.relatedTarget),
23 | $list = $popup.find('ol[data-control="records"]').first();
24 |
25 | // Inline the list height to prevent the modal from
26 | // shrinking while categories are being re-ordered
27 | $list.css('height', $list.height());
28 |
29 | // Submit the request on apply button clicks
30 | $popup.find('button[data-control="apply-btn"]').on('click', function() {
31 | var $loadingIndicator = $popup.find('div.loading-indicator'),
32 | treeData = [],
33 | i = 0;
34 |
35 | // Cycle through the list items and create the array of
36 | // data to send to the ajax handler.
37 | $list.find('li').each(function() {
38 | treeData[i] = {
39 | id: parseInt($(this).data('record-id')) || null,
40 | parent_id: parseInt($(this).parent().data('parent-id')) || null
41 | };
42 | i++;
43 | });
44 |
45 | $loadingIndicator.show();
46 | $.request('onUpdateTree', {
47 | data: {
48 | treeData: treeData,
49 | },
50 | success: function(data) {
51 | this.success(data).done(function() {
52 | $popup.trigger('close.oc.popup');
53 | $(document).trigger('render');
54 | });
55 | },
56 | complete: function(data) {
57 | $loadingIndicator.hide();
58 | }
59 | });
60 | });
61 | });
62 |
63 | // Trigger the popup
64 | this.$el.popup({
65 | handler: 'treesort::onLoadPopup'
66 | });
67 | }
68 |
69 | //
70 | // Attach TreeSort to the element
71 | //
72 | $.fn.TreeSort = function () {
73 | return new TreeSort(this);
74 | }
75 |
76 | $(document).on('render', function() {
77 | $('[data-control="treesort"]').TreeSort();
78 | });
79 |
80 | }(window.jQuery);
81 |
--------------------------------------------------------------------------------
/src/FormWidgets/Hasmany/README.md:
--------------------------------------------------------------------------------
1 | # Hasmany
2 | Has-many/belongs-to popup widget for [OctoberCMS](http://octobercms.com).
3 |
4 | 
5 |
6 | ### Installation
7 | To install the Hasmany widget, add the following to your plugin's ```composer.json``` file.
8 | ```json
9 | "require": {
10 | "owl/hasmany": "~1.0@dev"
11 | }
12 | ```
13 | Next, register the widget in your ```Plugin.php``` file.
14 | ```php
15 | public function registerFormWidgets()
16 | {
17 | return [
18 | 'Owl\FormWidgets\HasMany\Widget' => [
19 | 'label' => 'Hasmany',
20 | 'code' => 'owl-hasmany'
21 | ],
22 | ];
23 | }
24 | ```
25 |
26 | ### Usage
27 | First things first, you'll need to have a pair of models related via a has-many / belongs-to relationship. From there, in your parent model's fields.yaml file use the relationship name as the field name, and ```owl-hasmany``` as the type.
28 | ```yaml
29 | relationship:
30 | type: owl-hasmany
31 | ```
32 |
33 | Next, you'll need to define the default parameters, or a custom partial. The default parameters will create a list that is very similar to the Sitemap plugin's UI. You may use basic twig markup inside these fields, variable names will reference model attributes. The ```icon``` option should be a valid [Font-Autumn](http://daftspunk.github.io/Font-Autumn/) icon class, or ```false```.
34 | ```yaml
35 | relationship:
36 | type: owl-hasmany
37 | default:
38 | icon: icon-file-o
39 | label: "{{ name }}"
40 | comment: "{{ description }}"
41 | ```
42 | To customize the widget appearance, you may also define a custom partial instead of the default.
43 | ```yaml
44 | relationship:
45 | type: owl-hasmany
46 | partial: @/plugins/author/plugin/models/relationship/_partial.htm
47 | ```
48 | There are a few additional parameters available to customize the widget's appearance. Defining a ```sortColumn``` enables drag-and-drop re-ordering. This value should reference the model's "order by" column name. Defining a ```formHeader``` will change the default header of popup windows. Defining an ```addLabel``` or ```addIcon``` will customize the appearance of the add button. ```addLabel```.
49 |
50 | Javascript events will be triggered when a popup window is opened or closed. Listening for these events can be useful when you wish to show / hide fields based on form values.
51 | ```javascript
52 | $(document).bind("owl.hasmany.opened", function(e, data) {
53 | // popup was opened
54 | });
55 | $(document).bind("owl.hasmany.closed", function(e, data) {
56 | // popup was closed
57 | });
58 | ```
59 | The ```data``` array will contain 3 keys. ```data.alias``` refers to the widget alias, ```data.item``` refers to the popup's cooresponding li element, and ```data.form``` references the popup form element.
60 |
--------------------------------------------------------------------------------
/src/FormWidgets/Knob/Widget.php:
--------------------------------------------------------------------------------
1 | prepareVars();
29 | return $this->makePartial('widget');
30 | }
31 |
32 | /**
33 | * Prepares the form widget view data
34 | */
35 | public function prepareVars()
36 | {
37 | $this->vars['name'] = $this->formField->getName();
38 | $this->vars['default'] = $default = $this->getConfig('default', 0);
39 | $this->vars['label'] = $this->getConfig('knobLabel');
40 | $this->vars['comment'] = $this->getConfig('knobComment');
41 | $this->vars['knobSettings'] = [
42 | 'angleArc' => $this->getConfig('angleArc', 360),
43 | 'angleOffset' => $this->getConfig('angleOffset', 0),
44 | 'bgColor' => '#'.$this->getConfig('bgColor', 'EEEEEE'),
45 | 'cursor' => $this->getConfig('cursor', 'false'),
46 | 'displayInput' => $this->getConfig('displayInput', 'true'),
47 | 'displayPrevious' => $this->getConfig('displayPrevious', 'false'),
48 | 'fgColor' => '#'.$this->getConfig('fgColor', '87CEEB'),
49 | 'font' => $this->getConfig('font', 'Open Sans'),
50 | 'fontWeight' => $this->getConfig('fontWeight', 'normal'),
51 | 'height' => $this->getConfig('width', 100),
52 | 'inputColor' => '#'.$this->getConfig('inputColor', '87CEEB'),
53 | 'lineCap' => $this->getConfig('linecap', 'default'),
54 | 'max' => $this->getConfig('max', 100),
55 | 'min' => $this->getConfig('min', 0),
56 | 'readOnly' => $this->getConfig('disabled', 'false'),
57 | 'rotation' => $this->getConfig('rotation', 'clockwise'),
58 | 'step' => $this->getConfig('step', 1),
59 | 'stopper' => $this->getConfig('stopper', 'true'),
60 | 'thickness' => $this->getConfig('thickness', 0.3),
61 | 'width' => $this->getConfig('width', 100),
62 | ];
63 |
64 | $this->vars['value'] = $this->getLoadValue() ?: $default;
65 | }
66 |
67 | /**
68 | * {@inheritDoc}
69 | */
70 | public function loadAssets()
71 | {
72 | $this->addCss('css/widget.css');
73 | $this->addJs('js/widget.js');
74 | }
75 |
76 | /**
77 | * {@inheritDoc}
78 | */
79 | public function getSaveValue($value)
80 | {
81 | return $value;
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/FormWidgets/Preview/Widget.php:
--------------------------------------------------------------------------------
1 | ', '', $result);
30 | $result = TagProcessor::instance()->processTags($result, $preview);
31 | return $result;
32 | }
33 |
34 | /**
35 | * {@inheritDoc}
36 | */
37 | public function render()
38 | {
39 | $this->vars['preview_html'] = $this->formatHtml($this->model->content, true);
40 | return $this->makePartial('widget');
41 | }
42 |
43 | /**
44 | * {@inheritDoc}
45 | */
46 | public function loadAssets()
47 | {
48 | $this->addJs('js/widget.js');
49 | }
50 |
51 | public function init()
52 | {
53 | $this->checkUploadPostback();
54 | }
55 | protected function checkUploadPostback()
56 | {
57 | if (!post('X_BLOG_IMAGE_UPLOAD'))
58 | return;
59 | $uploadedFileName = null;
60 | try {
61 | $uploadedFile = Input::file('file');
62 | if ($uploadedFile)
63 | $uploadedFileName = $uploadedFile->getClientOriginalName();
64 | $validationRules = ['max:'.File::getMaxFilesize()];
65 | $validationRules[] = 'mimes:jpg,jpeg,bmp,png,gif';
66 | $validation = Validator::make(
67 | ['file_data' => $uploadedFile],
68 | ['file_data' => $validationRules]
69 | );
70 | if ($validation->fails())
71 | throw new ValidationException($validation);
72 | if (!$uploadedFile->isValid())
73 | throw new SystemException(Lang::get('cms::lang.asset.file_not_valid'));
74 | $fileRelation = $this->model->content_images();
75 | $file = new File();
76 | $file->data = $uploadedFile;
77 | $file->is_public = true;
78 | $file->save();
79 | $fileRelation->add($file, $this->sessionKey);
80 | $result = [
81 | 'file' => $uploadedFileName,
82 | 'path' => $file->getPath()
83 | ];
84 | $response = Response::make()->setContent($result);
85 | $response->send();
86 | die();
87 | } catch (Exception $ex) {
88 | $message = $uploadedFileName
89 | ? Lang::get('cms::lang.asset.error_uploading_file', ['name' => $uploadedFileName, 'error' => $ex->getMessage()])
90 | : $ex->getMessage();
91 | $result = [
92 | 'error' => $message,
93 | 'file' => $uploadedFileName
94 | ];
95 | $response = Response::make()->setContent($result);
96 | $response->send();
97 | die();
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/src/FormWidgets/Hasmany/widget/assets/css/hasmany.css:
--------------------------------------------------------------------------------
1 | /* Modal */
2 | div[data-owl="hasmany"] .modal-footer.in-progress button {
3 | visibility: hidden;
4 | }
5 | div[data-owl="hasmany"] .modal-footer [data-control="loading-indicator"] {
6 | bottom: 5px;
7 | position: absolute;
8 | right: 0;
9 | }
10 |
11 | /* Add button */
12 | div[data-owl="hasmany"].default a[data-control="add-model"] {
13 | display: block;
14 | margin-top: 20px;
15 | padding: 13px 15px;
16 | border: dotted 2px #ebebeb;
17 | color: #bdc3c7;
18 | font-size: 12px;
19 | font-weight: 600;
20 | text-transform: uppercase;
21 | -webkit-border-radius: 5px;
22 | -moz-border-radius: 5px;
23 | border-radius: 5px;
24 | vertical-align: middle;
25 | }
26 | .compact-container div[data-owl="hasmany"].default a[data-control="add-model"] {
27 | margin-left: 20px;
28 | margin-right: 20px;
29 | }
30 | div[data-owl="hasmany"].default a[data-control="add-model"] i {
31 | margin-right: 10px;
32 | font-size: 14px;
33 | }
34 | div[data-owl="hasmany"].default a[data-control="add-model"]:hover {
35 | text-decoration: none;
36 | background-color: #58b6f7 !important;
37 | color: #ffffff !important;
38 | border: none;
39 | padding: 15px 17px;
40 | }
41 |
42 | /* List */
43 | div[data-owl="hasmany"].default ol {
44 | list-style-type: none;
45 | margin: 0;
46 | padding: 0;
47 | font-size: 13px;
48 | }
49 | div[data-owl="hasmany"].default ol li {
50 | position: relative;
51 | height: 60px;
52 | color: #666;
53 | }
54 | div[data-owl="hasmany"].default ol li.delete-highlight {
55 | background: #C30;
56 | color: #fefefe;
57 | }
58 | div[data-owl="hasmany"].default ol li:hover {
59 | background: #58B6F7;
60 | color: #fefefe;
61 | cursor: pointer;
62 | }
63 | div[data-owl="hasmany"].default ol li .item {
64 | display: table;
65 | height: 60px;
66 | width: 100%;
67 | position: absolute;
68 | }
69 | div[data-owl="hasmany"].default ol li .icon {
70 | display: table-cell;
71 | vertical-align: middle;
72 | font-size: 22px;
73 | }
74 | div[data-owl="hasmany"].default ol li .icon i {
75 | display: block;
76 | width: 60px;
77 | text-align: center;
78 | }
79 | div[data-owl="hasmany"].default ol li .content {
80 | display: table-cell;
81 | vertical-align: middle;
82 | width: 50%;
83 | }
84 | div[data-owl="hasmany"].default ol li .content.no-icon {
85 | padding-left: 15px;
86 | }
87 |
88 | /* List sorting */
89 | div[data-owl="hasmany"].default ol.sorting li {
90 | background: #F9F9F9 !important;
91 | color: #666 !important;
92 | }
93 | div[data-owl="hasmany"].default ol.sorting li .actions {
94 | visibility: hidden !important;
95 | }
96 | div[data-owl="hasmany"].default ol li.sortable-ghost {
97 | background: #58B6F7 !important;
98 | color: #fefefe !important;
99 | z-index: 999;
100 | }
101 |
102 | /* List actions */
103 | div[data-owl="hasmany"].default ol li .actions {
104 | display: table-cell;
105 | vertical-align: middle;
106 | text-align: right;
107 | width: 50%;
108 | }
109 | div[data-owl="hasmany"].default ol li .actions a {
110 | font-size: 22px;
111 | float: right;
112 | margin-left: 1px;
113 | width: 60px;
114 | height: 60px;
115 | line-height: 60px;
116 | display: none;
117 | }
118 | div[data-owl="hasmany"].default ol li:hover .actions a, ol .sortable-placeholder .actions a {
119 | background: #2581B8;
120 | text-align: center;
121 | border-right: 1px solid #328ec8;
122 | text-decoration: none;
123 | color: #fefefe;
124 | display: inline-block;
125 | }
126 | div[data-owl="hasmany"].default ol li .actions [data-control="sort-handle"] {
127 | cursor: move;
128 | }
129 |
--------------------------------------------------------------------------------
/src/FormWidgets/Tagbox/Widget.php:
--------------------------------------------------------------------------------
1 | prepareVars();
15 | return $this->makePartial('widget');
16 | }
17 |
18 | /**
19 | * Prepare widget variables
20 | */
21 | public function prepareVars()
22 | {
23 | // Break key codes
24 | if (isset($this->config->breakCodes)) {
25 | $config['breakCodes'] = is_array($this->config->breakCodes)
26 | ? $this->config->breakCodes
27 | : [$this->config->breakCodes];
28 | } else {
29 | $config['breakCodes'] = [13, 9];
30 | }
31 |
32 | // Slugify
33 | $config['slugify'] = isset($this->config->slugify) &&
34 | filter_var($this->config->slugify, FILTER_VALIDATE_BOOLEAN);
35 |
36 | // Accepted characters
37 | $config['filter'] = isset($this->config->filter)
38 | ? $this->config->filter
39 | : false;
40 |
41 | // Validation rules
42 | $config['validation'] = isset($this->config->validation)
43 | ? $this->config->validation
44 | : false;
45 |
46 | // Validation message
47 | $config['validationMessage'] = isset($this->config->validationMessage)
48 | ? $this->config->validationMessage
49 | : 'The tag format is invalid.';
50 |
51 | // Disable auto-focus
52 | $config['autofocus'] = isset($this->config->autofocus)
53 | ? (bool) $this->config->autofocus
54 | : true;
55 |
56 | // Javascript configuration
57 | $config['fieldName'] = $this->fieldName;
58 | $this->vars['config'] = htmlspecialchars(json_encode($config));
59 |
60 | // Pre-populated tags
61 | $fieldName = $this->fieldName;
62 | $this->vars['tags'] = is_array($this->model->$fieldName)
63 | ? implode(',', $this->model->$fieldName)
64 | : false;
65 |
66 | // Placeholder
67 | $this->vars['placeholder'] = isset($this->config->placeholder)
68 | ? htmlspecialchars($this->config->placeholder)
69 | : "Enter tags...";
70 |
71 | // Prepopulated tags
72 | $tags = [];
73 | if (is_array($this->model->$fieldName) && count($this->model->$fieldName)) {
74 | foreach ($this->model->$fieldName as $tag)
75 | $tags[] = htmlspecialchars($tag);
76 | } else if ($loadValue = $this->getLoadValue()) {
77 | $loadValue = json_decode($loadValue, true);
78 | if ($loadValue && is_array($loadValue)) {
79 | foreach ($loadValue as $tag) {
80 | if (empty($tag))
81 | continue;
82 | $tags[] = htmlspecialchars($tag);
83 | }
84 | }
85 | }
86 |
87 | $this->vars['tags'] = $tags;
88 | }
89 |
90 | /**
91 | * Load widget assets
92 | */
93 | public function loadAssets()
94 | {
95 | $this->addJs('js/tagbox.js');
96 |
97 | if (isset($this->config->cssPath) && $this->config->cssPath)
98 | $this->addCss($this->config->cssPath);
99 |
100 | elseif (!isset($this->config->cssPath))
101 | $this->addCss('css/tagbox.css');
102 | }
103 |
104 | /**
105 | * Return save value
106 | * @return array
107 | */
108 | public function getSaveValue($value)
109 | {
110 | $data = [];
111 | foreach (post($this->fieldName) as $field) {
112 | if (empty($field))
113 | continue;
114 | $data[] = $field;
115 | }
116 |
117 | return $data;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/FormWidgets/Tagbox/widget/assets/js/tagbox.js:
--------------------------------------------------------------------------------
1 | +function ($) { "use strict";
2 |
3 | /**
4 | * Constructor
5 | */
6 | var tagbox = function (el, config) {
7 | this.$el = $(el)
8 |
9 | this.alias = this.$el.data('alias')
10 | this.config = this.$el.data('config')
11 | this.$tags = this.$el.find('[data-control="tags"]')
12 | this.$list = this.$el.find('[data-control="list"]')
13 | this.$last = this.$el.find('[data-control="last"]')
14 | this.$input = this.$el.find('[data-control="tagbox-input"]')
15 | this.$template = this.$el.find('[data-control="template"]')
16 |
17 | this.init()
18 | }
19 |
20 | /**
21 | * Listen for editor events
22 | */
23 | tagbox.prototype.init = function () {
24 | var self = this
25 |
26 | // Listen for break keys
27 | this.$input.unbind().on('keydown', function(e) {
28 | var code = e.keyCode || e.which
29 | if ($.inArray(code, self.config.breakCodes) !== -1) {
30 | e.preventDefault()
31 | self.addTag($(this).val())
32 | }
33 |
34 | // Listen for backspace removals
35 | if (code == 8 && $(this).val() === '') {
36 | self.backspaceRemove()
37 | return false
38 | }
39 |
40 | // Remove any pre-delete classes
41 | self.$list.find('.pre-delete').removeClass('pre-delete')
42 | })
43 |
44 | // Filter input
45 | this.$input.on('change keydown keyup paste', function() {
46 | self.filterInput()
47 | })
48 |
49 | // Listen for tag removals through X button
50 | this.$list.on('click', '[data-control="remove"]', function() {
51 | $(this).closest('li').remove()
52 | })
53 |
54 | // Focus the cursor in the input box
55 | if (this.config.autofocus) {
56 | this.$list.on('click', function() {
57 | self.$input.focus()
58 | })
59 | this.$input.on('focus', function() {
60 | self.$list.addClass('focused')
61 | })
62 | this.$input.on('blur', function() {
63 | self.$list.removeClass('focused')
64 | })
65 | }
66 | }
67 |
68 | /**
69 | * Removes invalid characters from input
70 | */
71 | tagbox.prototype.filterInput = function() {
72 | var filter = new RegExp(this.config.filter, 'g'),
73 | original = this.$input.val()
74 |
75 | this.$input.val(original.replace(filter, ''))
76 | }
77 |
78 | /**
79 | * Add tag to list
80 | */
81 | tagbox.prototype.addTag = function(tag) {
82 | this.filterInput()
83 |
84 | if (typeof tag != 'undefined' && !tag.length) {
85 | return false
86 | }
87 |
88 | // Validate the tag
89 | if (this.config.validation && this.validation(tag) == false) {
90 | $.oc.flashMsg({
91 | text: this.config.validationMessage,
92 | 'class': 'error',
93 | 'interval': 3
94 | })
95 | this.$input.focus()
96 | return false
97 | }
98 |
99 | // Sluggify the tag
100 | if (this.config.slugify) {
101 | tag = tag.toLowerCase()
102 | .replace(/\s+/g, '-') // Replace spaces with hyphens
103 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars
104 | .replace(/\-\-+/g, '-') // Replace multiple hyphens
105 | .replace(/^-+/, '') // Trim hyphens from start of text
106 | .replace(/-+$/, '') // Trim hyphens from end of text
107 | }
108 |
109 | var cleanTag = tag
110 | .replace(/&/g, "&")
111 | .replace(//g, ">")
113 | .replace(/"/g, """)
114 | .replace(/'/g, "'"),
115 | $exists = this.$list.find('[data-tag="' + cleanTag + '"]'),
116 | $newTag = $(this.$template.html())
117 |
118 | // Make sure the tag doesn't already exist
119 | if ($exists.length) {
120 | this.flash($exists)
121 | return false
122 | }
123 |
124 | // Add the new tag and clear the input box
125 | $newTag.attr('data-tag', cleanTag)
126 | $newTag.find('input').val(tag)
127 | $newTag.find('[data-control="display"]').html(cleanTag)
128 |
129 | this.$last.before($newTag)
130 | this.$input.val('')
131 | }
132 |
133 | /**
134 | * Removes a tag with the backspace key
135 | */
136 | tagbox.prototype.backspaceRemove = function() {
137 | var $target = this.$list.find('li:nth-last-child(2)')
138 |
139 | if ($target.hasClass('pre-delete')) {
140 | $target.remove()
141 | } else {
142 | $target.addClass('pre-delete')
143 | }
144 | }
145 |
146 | /**
147 | * Temporarily adds the flash class to a tag
148 | * @param element $tag
149 | */
150 | tagbox.prototype.flash = function($tag) {
151 | $tag.addClass('flash');
152 | setTimeout(function() {
153 | $tag.removeClass('flash');
154 | }, 300)
155 | }
156 |
157 | /**
158 | * Validates a tag
159 | * @return boolean
160 | */
161 | tagbox.prototype.validation = function(tag) {
162 | var expression = new RegExp(this.config.validation)
163 | return expression.test(tag)
164 | }
165 |
166 | /*
167 | * Bind and construct non-conflicting tagbox
168 | */
169 | var old = $.fn.tagbox
170 |
171 | $.fn.tagbox = function (config) {
172 | return new tagbox($(this), config)
173 | }
174 |
175 | $.fn.tagbox.Constructor = tagbox
176 |
177 | $.fn.tagbox.noConflict = function () {
178 | $.fn.tagbox = old
179 | return this
180 | }
181 |
182 | }(window.jQuery);
183 |
--------------------------------------------------------------------------------
/src/FormWidgets/Password/widget/assets/js/password.min.js:
--------------------------------------------------------------------------------
1 | (function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],factory)}else if(typeof exports==="object"){factory(require("jquery"))}else{factory(jQuery)}})(function($,undef){var dataKey="plugin_hideShowPassword",shorthandArgs=["show","innerToggle"],SPACE=32,ENTER=13;var canSetInputAttribute=function(){var body=document.body,input=document.createElement("input"),result=true;if(!body){body=document.createElement("body")}input=body.appendChild(input);try{input.setAttribute("type","text")}catch(e){result=false}body.removeChild(input);return result}();var defaults={show:"infer",innerToggle:false,enable:canSetInputAttribute,className:"hideShowPassword-field",initEvent:"hideShowPasswordInit",changeEvent:"passwordVisibilityChange",props:{autocapitalize:"off",autocomplete:"off",autocorrect:"off",spellcheck:"false"},toggle:{element:'',className:"hideShowPassword-toggle",touchSupport:typeof Modernizr==="undefined"?false:Modernizr.touch,attachToEvent:"click.hideShowPassword",attachToTouchEvent:"touchstart.hideShowPassword mousedown.hideShowPassword",attachToKeyEvent:"keyup",attachToKeyCodes:true,styles:{position:"absolute"},touchStyles:{pointerEvents:"none"},position:"infer",verticalAlign:"middle",offset:0,attr:{role:"button","aria-label":"Show Password",tabIndex:0}},wrapper:{element:"",className:"hideShowPassword-wrapper",enforceWidth:true,styles:{position:"relative"},inheritStyles:["display","verticalAlign","marginTop","marginRight","marginBottom","marginLeft"],innerElementStyles:{marginTop:0,marginRight:0,marginBottom:0,marginLeft:0}},states:{shown:{className:"hideShowPassword-shown",changeEvent:"passwordShown",props:{type:"text"},toggle:{className:"hideShowPassword-toggle-hide",content:"Hide",attr:{"aria-pressed":"true"}}},hidden:{className:"hideShowPassword-hidden",changeEvent:"passwordHidden",props:{type:"password"},toggle:{className:"hideShowPassword-toggle-show",content:"Show",attr:{"aria-pressed":"false"}}}}};function HideShowPassword(element,options){this.element=$(element);this.wrapperElement=$();this.toggleElement=$();this.init(options)}HideShowPassword.prototype={init:function(options){if(this.update(options,defaults)){this.element.addClass(this.options.className);if(this.options.innerToggle){this.wrapElement(this.options.wrapper);this.initToggle(this.options.toggle);if(typeof this.options.innerToggle==="string"){this.toggleElement.hide();this.element.one(this.options.innerToggle,$.proxy(function(){this.toggleElement.show()},this))}}this.element.trigger(this.options.initEvent,[this])}},update:function(options,base){this.options=this.prepareOptions(options,base);if(this.updateElement()){this.element.trigger(this.options.changeEvent,[this]).trigger(this.state().changeEvent,[this])}return this.options.enable},toggle:function(showVal){showVal=showVal||"toggle";return this.update({show:showVal})},prepareOptions:function(options,base){var keyCodes=[],testElement;base=base||this.options;options=$.extend(true,{},base,options);if(options.enable){if(options.show==="toggle"){options.show=this.isType("hidden",options.states)}else if(options.show==="infer"){options.show=this.isType("shown",options.states)}if(options.toggle.position==="infer"){options.toggle.position=this.element.css("text-direction")==="rtl"?"left":"right"}if(!$.isArray(options.toggle.attachToKeyCodes)){if(options.toggle.attachToKeyCodes===true){testElement=$(options.toggle.element);switch(testElement.prop("tagName").toLowerCase()){case"button":case"input":break;case"a":if(testElement.filter("[href]").length){keyCodes.push(SPACE);break}default:keyCodes.push(SPACE,ENTER);break}}options.toggle.attachToKeyCodes=keyCodes}}return options},updateElement:function(){if(!this.options.enable||this.isType())return false;this.element.prop($.extend({},this.options.props,this.state().props)).addClass(this.state().className).removeClass(this.otherState().className);this.updateToggle();return true},isType:function(comparison,states){states=states||this.options.states;comparison=comparison||this.state(undef,undef,states).props.type;if(states[comparison]){comparison=states[comparison].props.type}return this.element.prop("type")===comparison},state:function(key,invert,states){states=states||this.options.states;if(key===undef){key=this.options.show}if(typeof key==="boolean"){key=key?"shown":"hidden"}if(invert){key=key==="shown"?"hidden":"shown"}return states[key]},otherState:function(key){return this.state(key,true)},wrapElement:function(options){var enforceWidth=options.enforceWidth,targetWidth;if(!this.wrapperElement.length){targetWidth=this.element.outerWidth();$.each(options.inheritStyles,$.proxy(function(index,prop){options.styles[prop]=this.element.css(prop)},this));this.element.css(options.innerElementStyles).wrap($(options.element).addClass(options.className).css(options.styles));this.wrapperElement=this.element.parent();if(enforceWidth===true){enforceWidth=this.wrapperElement.outerWidth()===targetWidth?false:targetWidth}if(enforceWidth!==false){this.wrapperElement.css("width",enforceWidth)}}return this.wrapperElement},initToggle:function(options){if(!this.toggleElement.length){this.toggleElement=$(options.element).attr(options.attr).addClass(options.className).css(options.styles).appendTo(this.wrapperElement);this.updateToggle();this.positionToggle(options.position,options.verticalAlign,options.offset);if(options.touchSupport){this.toggleElement.css(options.touchStyles);this.element.on(options.attachToTouchEvent,$.proxy(this.toggleTouchEvent,this))}else{this.toggleElement.on(options.attachToEvent,$.proxy(this.toggleEvent,this))}if(options.attachToKeyCodes.length){this.toggleElement.on(options.attachToKeyEvent,$.proxy(this.toggleKeyEvent,this))}}return this.toggleElement},positionToggle:function(position,verticalAlign,offset){var styles={};styles[position]=offset;switch(verticalAlign){case"top":case"bottom":styles[verticalAlign]=offset;break;case"middle":styles.top="50%";styles.marginTop=this.toggleElement.outerHeight()/-2;break}return this.toggleElement.css(styles)},updateToggle:function(state,otherState){var paddingProp,targetPadding;if(this.toggleElement.length){paddingProp="padding-"+this.options.toggle.position;state=state||this.state().toggle;otherState=otherState||this.otherState().toggle;this.toggleElement.attr(state.attr).addClass(state.className).removeClass(otherState.className).html(state.content);targetPadding=this.toggleElement.outerWidth()+this.options.toggle.offset*2;if(this.element.css(paddingProp)!==targetPadding){this.element.css(paddingProp,targetPadding)}}return this.toggleElement},toggleEvent:function(event){event.preventDefault();this.toggle()},toggleKeyEvent:function(event){$.each(this.options.toggle.attachToKeyCodes,$.proxy(function(index,keyCode){if(event.which===keyCode){this.toggleEvent(event);return false}},this))},toggleTouchEvent:function(event){var toggleX=this.toggleElement.offset().left,eventX,lesser,greater;if(toggleX){eventX=event.pageX||event.originalEvent.pageX;if(this.options.toggle.position==="left"){toggleX+=this.toggleElement.outerWidth();lesser=eventX;greater=toggleX}else{lesser=toggleX;greater=eventX}if(greater>=lesser){this.toggleEvent(event)}}}};$.fn.hideShowPassword=function(){var options={};$.each(arguments,function(index,value){var newOptions={};if(typeof value==="object"){newOptions=value}else if(shorthandArgs[index]){newOptions[shorthandArgs[index]]=value}else{return false}$.extend(true,options,newOptions)});return this.each(function(){var $this=$(this),data=$this.data(dataKey);if(data){data.update(options)}else{$this.data(dataKey,new HideShowPassword(this,options))}})};$.each({show:true,hide:false,toggle:"toggle"},function(verb,showVal){$.fn[verb+"Password"]=function(innerToggle,options){return this.hideShowPassword(showVal,innerToggle,options)}})});
2 |
--------------------------------------------------------------------------------
/src/FormWidgets/Preview/widget/assets/js/widget.js:
--------------------------------------------------------------------------------
1 | +function ($) { "use strict";
2 | var PostForm = function () {
3 | this.$preview = $('#blog-post-preview')
4 | this.$form = this.$preview.closest('form')
5 | this.formAction = this.$form.attr('action')
6 | this.sessionKey = $('input[name=_session_key]', this.$form).val()
7 | this.$textarea = $('[name="Post[content]"]', this.$form)
8 | this.$previewContent = $('.preview-content', this.$preview)
9 | this.codeEditor = $('textarea[name="Post[content]"]', this.$form).closest('.field-codeeditor').data('oc.codeEditor')
10 | this.createIndicator()
11 |
12 | this.$textarea.on('oc.codeEditorChange', $.proxy(this.handleChange, this))
13 |
14 | this.loading = false
15 | this.updatesPaused = false
16 | this.initPreview()
17 | this.initDropzones()
18 | this.initFormEvents()
19 | this.initLayout()
20 | }
21 |
22 | PostForm.prototype.handleChange = function() {
23 | if (this.updatesPaused)
24 | return
25 |
26 | var self = this
27 |
28 | if (this.loading) {
29 | if (this.dataTrackInputTimer === undefined) {
30 | this.dataTrackInputTimer = window.setInterval(function(){
31 | self.handleChange()
32 | }, 100)
33 | }
34 |
35 | return
36 | }
37 |
38 | window.clearTimeout(this.dataTrackInputTimer)
39 | this.dataTrackInputTimer = undefined
40 |
41 | var self = this;
42 | self.update();
43 | }
44 |
45 | PostForm.prototype.createIndicator = function() {
46 | var $previewContainer = $('#blog-post-preview').closest('.loading-indicator-container')
47 | this.$indicator = $('
')
48 | $previewContainer.prepend(this.$indicator)
49 | }
50 |
51 | PostForm.prototype.update = function() {
52 | var self = this
53 |
54 | this.loading = true
55 | this.showIndicator()
56 |
57 | this.$form.request('onRefreshPreview', {
58 | success: function(data) {
59 | self.$previewContent.html(data.preview)
60 | self.initPreview()
61 | self.updateScroll()
62 | }
63 | }).done(function(){
64 | self.hideIndicator()
65 | self.loading = false
66 | })
67 | }
68 |
69 | PostForm.prototype.showIndicator = function() {
70 | this.$indicator.css('display', 'block')
71 | }
72 |
73 | PostForm.prototype.hideIndicator = function() {
74 | this.$indicator.css('display', 'none')
75 | }
76 |
77 | PostForm.prototype.initPreview = function() {
78 | prettyPrint()
79 | this.initImageUploaders()
80 | }
81 |
82 | PostForm.prototype.updateScroll = function() {
83 | this.$preview.data('oc.scrollbar').update()
84 | }
85 |
86 | PostForm.prototype.initImageUploaders = function() {
87 | var self = this
88 | $('span.image-placeholder .upload-dropzone', this.$preview).each(function(){
89 | var
90 | $placeholder = $(this).parent(),
91 | $link = $('span.label', $placeholder),
92 | placeholderIndex = $placeholder.data('index')
93 |
94 | var dropzone = new Dropzone($(this).get(0), {
95 | url: self.formAction,
96 | clickable: [$(this).get(0), $link.get(0)],
97 | previewsContainer: $('
').get(0),
98 | paramName: 'file'
99 | })
100 |
101 | dropzone.on('error', function(file, error) {
102 | alert('Error uploading file: ' + error)
103 | })
104 | dropzone.on('success', function(file, data){
105 | if (data.error)
106 | alert(data.error)
107 | else {
108 | self.pauseUpdates()
109 | var $img = $('
')
110 | $img.load(function(){
111 | self.updateScroll()
112 | })
113 |
114 | $placeholder.replaceWith($img)
115 |
116 | self.codeEditor.editor.replace('', {
117 | needle: ''
118 | })
119 | self.resumeUpdates()
120 | }
121 | })
122 | dropzone.on('complete', function(){
123 | $placeholder.removeClass('loading')
124 | })
125 | dropzone.on('sending', function(file, xhr, formData) {
126 | formData.append('X_BLOG_IMAGE_UPLOAD', 1)
127 | formData.append('_session_key', self.sessionKey)
128 | $placeholder.addClass('loading')
129 | })
130 | })
131 | }
132 |
133 | PostForm.prototype.pauseUpdates = function() {
134 | this.updatesPaused = true
135 | }
136 |
137 | PostForm.prototype.resumeUpdates = function() {
138 | this.updatesPaused = false
139 | }
140 |
141 | PostForm.prototype.initDropzones = function() {
142 | $(document).bind('dragover', function (e) {
143 | var dropZone = $('span.image-placeholder .upload-dropzone'),
144 | foundDropzone,
145 | timeout = window.dropZoneTimeout
146 |
147 | if (!timeout)
148 | dropZone.addClass('in');
149 | else
150 | clearTimeout(timeout);
151 |
152 | var found = false,
153 | node = e.target
154 |
155 | do {
156 | if ($(node).hasClass('dropzone')) {
157 | found = true
158 | foundDropzone = $(node)
159 | break
160 | }
161 |
162 | node = node.parentNode;
163 |
164 | } while (node != null);
165 |
166 | dropZone.removeClass('in hover')
167 |
168 | if (found)
169 | foundDropzone.addClass('hover')
170 |
171 | window.dropZoneTimeout = setTimeout(function () {
172 | window.dropZoneTimeout = null
173 | dropZone.removeClass('in hover')
174 | }, 100)
175 | })
176 | }
177 |
178 | PostForm.prototype.initFormEvents = function() {
179 | $(document).on('ajaxSuccess', '#post-form', function(event, context, data){
180 | if (context.handler == 'onSave' && !data.X_OCTOBER_ERROR_FIELDS) {
181 | $(this).trigger('unchange.oc.changeMonitor')
182 | }
183 | })
184 |
185 | $('#DatePicker-formPublishedAt-input-published_at').triggerOn({
186 | triggerCondition: 'checked',
187 | trigger: '#Form-field-Post-published',
188 | triggerType: 'enable'
189 | })
190 | }
191 |
192 | PostForm.prototype.initLayout = function() {
193 | $('#Form-secondaryTabs .tab-pane.layout-cell:not(:first-child)').addClass('padded-pane')
194 | }
195 |
196 | PostForm.prototype.replacePlaceholder = function(placeholder, placeholderHtmlReplacement, mdCodePlaceholder, mdCodeReplacement) {
197 | this.pauseUpdates()
198 | placeholder.replaceWith(placeholderHtmlReplacement)
199 |
200 | this.codeEditor.editor.replace(mdCodeReplacement, {
201 | needle: mdCodePlaceholder
202 | })
203 | this.updateScroll()
204 | this.resumeUpdates()
205 | }
206 |
207 | $(document).ready(function(){
208 | var form = new PostForm()
209 |
210 | if ($.oc === undefined)
211 | $.oc = {}
212 |
213 | $.oc.blogPostForm = form
214 | })
215 |
216 | }(window.jQuery);
--------------------------------------------------------------------------------
/src/FormWidgets/Hasmany/Widget.php:
--------------------------------------------------------------------------------
1 | model->hasMany[$this->fieldName]))
28 | throw new Exception('Unknown model relationship "'.$this->fieldName.'".');
29 |
30 | // Make sure the default style or a custom partial was defined
31 | if (!isset($this->config->default) && !isset($this->config->partial))
32 | throw new Exception('Default properties or a custom partial must be defined.');
33 |
34 | $this->relatedModel = new $this->model->hasMany[$this->fieldName][0];
35 | }
36 |
37 | /**
38 | * Render the widget
39 | * @return $this->makePartial()
40 | */
41 | public function render()
42 | {
43 | $this->prepareVars();
44 | return $this->makePartial('widget');
45 | }
46 |
47 | /**
48 | * {@inheritDoc}
49 | */
50 | public function loadAssets()
51 | {
52 | $this->addCss('css/hasmany.css');
53 | $this->addJs('js/sortable.min.js');
54 | $this->addJs('js/jquery-binding.js');
55 | $this->addJs('js/hasmany.js', 'core');
56 | }
57 |
58 | /**
59 | * Loads relationship
60 | */
61 | private function loadRelationship()
62 | {
63 | // Explode model path for determining plugin path
64 | $fieldName = $this->fieldName;
65 | $parts = explode('\\', strtolower($this->model->hasMany[$fieldName][0]));
66 |
67 | // Load relationship model, and configure the widget
68 | $relationship = $this->makeConfig("@/plugins/$parts[0]/$parts[1]/models/$parts[3]/fields.yaml");
69 | $relationship->model = $this->relatedModel;
70 | $relationship->alias = $this->alias.$fieldName;
71 |
72 | // Load form header
73 | if (isset($this->config->formLabel))
74 | $this->vars['formLabel'] = htmlspecialchars($this->config->formLabel);
75 | else {
76 | $modelName = explode('\\', get_class($this->relatedModel));
77 | $this->vars['formLabel'] = end($modelName);
78 | }
79 |
80 | return $relationship;
81 | }
82 |
83 | /**
84 | * {@inheritDoc}
85 | */
86 | public function prepareVars()
87 | {
88 | $this->vars['alias'] = $this->alias;
89 |
90 | $relationship = $this->loadRelationship();
91 | $this->vars['formWidget'] = $this->makeWidget('Backend\Widgets\Form', $relationship);
92 |
93 | $partials = [];
94 | $fieldName = $this->fieldName;
95 | foreach ($this->model->$fieldName as $item)
96 | $partials[] = $this->loadItemPartial($item);
97 | $this->vars['items'] = $partials;
98 |
99 | // Add button icon and label
100 | $this->vars['addIcon'] = isset($this->config->addIcon)
101 | ? $this->config->addIcon
102 | : 'icon-plus';
103 | $this->vars['addLabel'] = isset($this->config->addLabel)
104 | ? $this->config->addLabel
105 | : 'Add '.$this->vars['formLabel'];
106 |
107 | $this->vars['default'] = isset($this->config->default);
108 |
109 | // Load config
110 | $config['sortable'] = isset($this->config->sortColumn);
111 | $this->vars['config'] = htmlspecialchars(json_encode($config));
112 | }
113 |
114 | /**
115 | * Renders the related form widget
116 | * @return array
117 | */
118 | public function onRenderForm()
119 | {
120 | // Load and popupate the related model
121 | $relationship = $this->loadRelationship();
122 | if ($data = input(':model')) {
123 | foreach ($data as $key => $value)
124 | $relationship->model->$key = $value;
125 | }
126 |
127 | $formWidget = $this->makeWidget('Backend\Widgets\Form', $relationship);
128 |
129 | return $this->makePartial('popup', ['formWidget' => $formWidget]);
130 | }
131 |
132 | /**
133 | * Validate the related model and return it's json data
134 | * @return array | bool (false)
135 | */
136 | public function onProcessForm()
137 | {
138 | // Find or new the model
139 | $model = ($id = input('id')) && $id
140 | ? $this->relatedModel->findOrNew($id)
141 | : new $this->relatedModel;
142 |
143 | // Popuplate and validate the model with our form data
144 | foreach (input() as $key => $value) {
145 | if ($key == 'id' || $key == 'created_at' || $key == 'updated_at')
146 | continue;
147 | $model->$key = $value;
148 | $extraData[$key] = $value;
149 | }
150 | $model->validate();
151 |
152 | // Render the partial and return it as our list item
153 | return [
154 | 'item' => $this->loadItemPartial($model, $extraData)
155 | ];
156 | }
157 |
158 | /**
159 | * Loads an item partial
160 | * @param Model $item
161 | * @return string
162 | */
163 | private function loadItemPartial($item, $extraData = false)
164 | {
165 | // Convert our item data to a html-safe json object
166 | $data = [];
167 | foreach ($item->toArray() as $key => $value) {
168 | // If the item is json, convert it to an array and filter empty values
169 | if (is_string($value)) {
170 | $jsonArray = json_decode($value, true);
171 | if (json_last_error() == JSON_ERROR_NONE && is_array($jsonArray))
172 | $value = array_filter($jsonArray);
173 | }
174 | $data[$key] = $value;
175 | }
176 |
177 | if ($extraData) {
178 | foreach ($extraData as $key => $value) {
179 | if (array_key_exists($key, $data))
180 | continue;
181 | $data[$key] = $value;
182 | }
183 | }
184 |
185 | $this->vars['modelData'] = htmlspecialchars(json_encode($data));
186 |
187 | $partialPath = isset($this->config->default)
188 | ? 'item'
189 | : $this->config->partial;
190 |
191 | $loader = new Twig_Loader_Array([
192 | 'item' => $this->makePartial($partialPath, ['item' => $item])
193 | ]);
194 |
195 | $twig = new Twig_Environment($loader);
196 |
197 | return $this->makePartial('input', ['item' => $item])
198 | .$twig->render('item', $item->toArray());
199 | }
200 |
201 | /**
202 | * Attach deferred bindings and execute deferred deletes
203 | * @param null $value
204 | * @return FormField
205 | */
206 | public function getSaveValue($value)
207 | {
208 | $formData = input($this->alias);
209 |
210 | // Create new models with deferred bindings, or update existing models
211 | $fieldName = $this->fieldName;
212 | if (isset($formData['models']) && count($formData['models'])) {
213 | foreach ($formData['models'] as $i => $data) {
214 | $data = json_decode($data, true);
215 | $model = isset($data['id'])
216 | ? $this->relatedModel->findOrNew($data['id'])
217 | : new $this->relatedModel;
218 |
219 | foreach ($data as $key => $value) {
220 | if ($key == 'id' || $key == 'created_at' || $key == 'updated_at')
221 | continue;
222 |
223 | $model->$key = $value;
224 | }
225 |
226 | if (isset($this->config->sortColumn)) {
227 | $sortColumn = $this->config->sortColumn;
228 | $model->$sortColumn = $i;
229 | }
230 |
231 | $model->save();
232 |
233 | if (!isset($data['id']))
234 | $this->model->$fieldName()->add($model, $this->sessionKey);
235 | }
236 | }
237 |
238 | // Deferred deletes
239 | $deleteIds = json_decode($formData['delete'], true);
240 | if (count($deleteIds)) {
241 | $this->model->bindEvent('model.afterSave', function() use ($deleteIds) {
242 | $this->relatedModel->whereIn('id', $deleteIds)->delete();
243 | });
244 | }
245 |
246 | return FormField::NO_SAVE_DATA;
247 | }
248 |
249 | }
250 |
--------------------------------------------------------------------------------
/src/FormWidgets/Address/widget/assets/js/location-autocomplete.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Location Autocomplete plugin
3 | *
4 | * Data attributes:
5 | * - data-control="locationAutocomplete" - enables the plugin on an element
6 | * - data-input-street="#locationStreet" - input to populate with street
7 | * - data-input-city="#locationCity" - input to populate with city
8 | * - data-input-zip="#locationZip" - input to populate with zip
9 | * - data-input-state="#locationState" - input to populate with state
10 | * - data-input-country="#locationCountry" - input to populate with country
11 | * - data-input-country-long="#locationCountry" - input to populate with country (long name)
12 | * - data-input-latitude="#locationLatitude" - input to populate with latitude
13 | * - data-input-longitude="#locationLongitude" - input to populate with longitude
14 | * - data-input-placename="#locationPlaceName" - input to populate with building name
15 | * - data-input-placeaddress="#locationPlaceAddress" - input to populate with building address
16 | * - data-input-formataddress="#locationFormatAddress" - input to populate with formatted building address
17 | *
18 | * JavaScript API:
19 | * $('input#addressAutocomplete').locationAutocomplete({ inputCountry: '#locationCountry' })
20 | *
21 | * Dependences:
22 | * - Google maps (http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false)
23 | *
24 | * Example markup:
25 | *
26 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | *
49 | */
50 |
51 |
52 | +function ($, map) { "use strict";
53 |
54 | // LOCATION AUTOCOMPLETE CLASS DEFINITION
55 | // ============================
56 |
57 | var LocationAutocomplete = function(element, options) {
58 | this.options = options
59 | this.$el = $(element)
60 |
61 | // Init
62 | this.init()
63 | }
64 |
65 | LocationAutocomplete.DEFAULTS = {
66 | inputLatitude: null,
67 | inputLongitude: null,
68 | inputStreet: null,
69 | inputCity: null,
70 | inputZip: null,
71 | inputState: null,
72 | inputCountry: null,
73 | inputCountryLong: null,
74 | inputName: null,
75 | inputAddress: null,
76 | inputFormatAddress: null
77 | }
78 |
79 | LocationAutocomplete.prototype.init = function() {
80 | this.autocomplete = new map.places.Autocomplete(this.$el.get(0), {
81 | types: ['geocode']
82 | })
83 |
84 | map.event.addListener(this.autocomplete, 'place_changed', $.proxy(this.handlePlaceChanged, this))
85 |
86 | // Prevent ENTER from submitting form
87 | this.$el.bind('keypress keydown keyup', function(e){
88 | if (e.keyCode == 13) { e.preventDefault() }
89 | })
90 | }
91 |
92 | LocationAutocomplete.prototype.getValueMap = function() {
93 | return {
94 | 'geometry': {
95 | inputLatitude: 'lat',
96 | inputLongitude: 'lng'
97 | },
98 | 'short': {
99 | inputStreet: ['street_number', 'route'],
100 | inputCity: 'locality',
101 | inputZip: 'postal_code',
102 | inputState: 'administrative_area_level_1',
103 | inputCountry: 'country'
104 | },
105 | 'long': {
106 | inputCountryLong: 'country'
107 | },
108 | 'place': {
109 | inputName: 'name',
110 | inputAddress: 'addr',
111 | inputFormataddress: 'fmtaddr'
112 | }
113 | }
114 | }
115 |
116 | LocationAutocomplete.prototype.handlePlaceChanged = function() {
117 |
118 | var self = this,
119 | place = this.autocomplete.getPlace(),
120 | geoLocation = place.geometry.location,
121 | valueMap = this.getValueMap()
122 |
123 | var extractionFunction = function(standard, google, resultType) {
124 | var value = []
125 |
126 | if (!$.isArray(google))
127 | google = [google]
128 |
129 | $.each(google, function(index, _google) {
130 | value.push(self.getValueFromAddressObject(place, _google))
131 | })
132 |
133 | return value.join(' ')
134 | }
135 |
136 | var elementFinderFunction = function(lookupKey) {
137 | if (self.options[lookupKey] === undefined)
138 | return
139 |
140 | var element = $(self.options[lookupKey])
141 |
142 | if (element.length == 0)
143 | return
144 |
145 | return element
146 | }
147 |
148 | $.each(valueMap['geometry'], function(standard, google){
149 | var $targetEl = elementFinderFunction(standard)
150 | if (!$targetEl) return
151 |
152 | if (google == 'lat') $targetEl.val(geoLocation.lat())
153 | else if (google == 'lng') $targetEl.val(geoLocation.lng())
154 | })
155 |
156 | $.each(valueMap['short'], function(standard, google){
157 | var $targetEl = elementFinderFunction(standard)
158 | if (!$targetEl) return
159 |
160 | $targetEl.val(extractionFunction(standard, google))
161 | })
162 |
163 | $.each(valueMap['long'], function(standard, google){
164 | var $targetEl = elementFinderFunction(standard)
165 | if (!$targetEl) return
166 |
167 | $targetEl.val(extractionFunction(standard, google, 'long_name'))
168 | })
169 |
170 | $.each(valueMap['place'], function(standard, google){
171 | var $targetEl = elementFinderFunction(standard)
172 | if (!$targetEl) return
173 |
174 | if (google == 'name') $targetEl.val(place.name)
175 | else if (google == 'addr') $targetEl.val(place.formatted_address)
176 | else if (google == 'fmtaddr') $targetEl.val(place.formatted_address.replace(/ /g,"+"))
177 | })
178 | }
179 |
180 | /*
181 | * Helper to spin through a Google address object and return
182 | * the value based on its type, eg: country
183 | *
184 | * Type guide:
185 | * -----------
186 | * street_number = Street Number
187 | * route = Street
188 | * locality = City
189 | * administrative_area_level_1 = State
190 | * country = Country
191 | * postal_code = Zip
192 | *
193 | */
194 | LocationAutocomplete.prototype.getValueFromAddressObject = function(addressObj, type, resultType) {
195 | var self = this
196 | var result = null
197 | if (!resultType)
198 | resultType = 'short_name'
199 |
200 | if (!addressObj)
201 | return null
202 |
203 | for (var i = 0; i < addressObj.address_components.length; i++) {
204 | for (var j = 0; j < addressObj.address_components[i].types.length; j++) {
205 | if (addressObj.address_components[i].types[j] == type)
206 | result = addressObj.address_components[i][resultType]
207 | }
208 | }
209 |
210 | return result
211 | }
212 |
213 | // LOCATION AUTOCOMPLETE PLUGIN DEFINITION
214 | // ============================
215 |
216 | var old = $.fn.locationAutocomplete
217 |
218 | $.fn.locationAutocomplete = function (option) {
219 | var args = Array.prototype.slice.call(arguments, 1), result
220 | this.each(function () {
221 | var $this = $(this)
222 | var data = $this.data('locationAutocomplete')
223 | var options = $.extend({}, LocationAutocomplete.DEFAULTS, $this.data(), typeof option == 'object' && option)
224 | if (!data) $this.data('locationAutocomplete', (data = new LocationAutocomplete(this, options)))
225 | if (typeof option == 'string') result = data[option].apply(data, args)
226 | if (typeof result != 'undefined') return false
227 | })
228 |
229 | return result ? result : this
230 | }
231 |
232 | $.fn.locationAutocomplete.Constructor = LocationAutocomplete
233 |
234 | // LOCATION AUTOCOMPLETE NO CONFLICT
235 | // =================
236 |
237 | $.fn.locationAutocomplete.noConflict = function () {
238 | $.fn.locationAutocomplete = old
239 | return this
240 | }
241 |
242 | // LOCATION AUTOCOMPLETE DATA-API
243 | // ===============
244 |
245 | $(document).render(function() {
246 | $('[data-control="locationAutocomplete"]').locationAutocomplete()
247 | })
248 |
249 | }(window.jQuery, google.maps);
--------------------------------------------------------------------------------
/src/FormWidgets/Hasmany/widget/assets/js/sortable.min.js:
--------------------------------------------------------------------------------
1 | /*! Sortable 1.0.1 - MIT | git://github.com/rubaxa/Sortable.git */
2 | !function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),L.forEach(function(d){b[d]=c(this,b[d]||M),f(a,d.substr(2).toLowerCase(),b[d])},this),a[E]=g.name+" "+(g.put.join?g.put.join(" "):"");for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),I&&f(a,"selectstart",this._onTapStart),f(a,"dragover",this._onDragOver),f(a,"dragenter",this._onDragOver),P.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=O.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(O.call(arguments)))}}function d(a,b,c){if(a){c=c||G,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return G.defaultView&&G.defaultView.getComputedStyle?c=G.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){J=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling)&&"TEMPLATE"!==a.nodeName.toUpperCase();)b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D={},E="Sortable"+(new Date).getTime(),F=window,G=F.document,H=F.parseInt,I=!!G.createElement("div").dragDrop,J=!1,K=function(a,b,c,d,e,f){var g=G.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},L="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),M=function(){},N=Math.abs,O=[].slice,P=[];return a.prototype={constructor:a,_dragStarted:function(){h(q,this.options.ghostClass,!0),a.active=this,K(t,"start",q,t,y)},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)){if(h.handle&&(e=d(e,h.handle,i)),e=d(e,h.draggable,i),y=o(e),"function"==typeof l){if(l.call(this,a,e,this))return K(g,"filter",e,i,y),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(K(a,"filter",e,i,y),!0):void 0})))return void a.preventDefault();if(e&&!q&&e.parentNode===i){"selectstart"===b&&e.dragDrop(),B=a,t=this.el,q=e,v=q.nextSibling,A=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(B={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(B,!0),a.preventDefault()),f(G,"mouseup",this._onDrop),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),f(G,"dragover",this);try{G.selection?G.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(C){i(r,"display","none");var a=G.elementFromPoint(C.clientX,C.clientY),b=a,c=this.options.group.name,d=P.length;if(b)do{if((" "+b[E]+" ").indexOf(c)>-1){for(;d--;)P[d]({clientX:C.clientX,clientY:C.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(B){var b=a.touches[0],c=b.clientX-B.clientX,d=b.clientY-B.clientY,e="translate3d("+c+"px,"+d+"px,0)";C=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),this._onDrag(b),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==A.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-H(h.marginTop,10)),i(r,"left",g.left-H(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),f(G,"touchmove",this._onTouchMove),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(G,"drop",this);if(u=d.scroll,u===!0){u=t;do if(u.offsetWidth
=i-g)-(e>=g),l=(e>=j-h)-(e>=h);k||l?b=F:u&&(b=u,c=u.getBoundingClientRect(),k=(N(c.right-g)<=e)-(N(c.left-g)<=e),l=(N(c.bottom-h)<=e)-(N(c.top-h)<=e)),(D.vx!==k||D.vy!==l||D.el!==b)&&(D.el=b,D.vx=k,D.vy=l,clearInterval(D.pid),b&&(D.pid=setInterval(function(){b===F?F.scrollTo(F.scrollX+k*f,F.scrollY+l*f):(l&&(b.scrollTop+=l*f),k&&(b.scrollLeft+=k*f))},24)))}},30),_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=A===j,o=h.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),!J&&A&&(n?o||(f=!t.contains(q)):A.pull&&k&&(A.name===j.name||k.indexOf&&~k.indexOf(A.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||v?t.insertBefore(q,s||v):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;u=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(u,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[E]){w!==c&&(w=c,x=i(c));var p,u=c.getBoundingClientRect(),y=u.right-u.left,z=u.bottom-u.top,B=/left|right|inline/.test(x.cssFloat+x.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,F=(B?(a.clientX-u.left)/y:(a.clientY-u.top)/z)>.5,G=c.nextElementSibling;J=!0,setTimeout(l,30),b(n),p=B?c.previousElementSibling===q&&!C||F&&C:G!==q&&!D||F&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(u,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),b.animated=!1},c)}},_offUpEvents:function(){g(G,"mouseup",this._onDrop),g(G,"touchmove",this._onTouchMove),g(G,"touchend",this._onDrop),g(G,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(D.pid),g(G,"drop",this),g(G,"dragover",this),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(z=o(q),K(q.parentNode,"sort",q,t,y,z),K(t,"sort",q,t,y,z),K(q,"add",q,t,y,z),K(t,"remove",q,t,y,z)):(s&&s.parentNode.removeChild(s),q.nextSibling!==v&&(z=o(q),K(t,"update",q,t,y,z),K(t,"sort",q,t,y,z))),a.active&&K(t,"end",q,t,y,z)),t=q=r=v=s=B=C=w=x=A=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b?(this._onDrag(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length;f>e;e++)a=c[e],d(a,this.options.draggable,this.el)&&b.push(a.getAttribute("data-id")||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;L.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"selectstart",this._onTapStart),g(a,"dragover",this._onDragOver),g(a,"dragenter",this._onDragOver),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),P.splice(P.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:K,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a});
3 |
--------------------------------------------------------------------------------
/src/FormWidgets/Hasmany/widget/assets/js/hasmany.js:
--------------------------------------------------------------------------------
1 | /**
2 | * October Widget Library - Hasmany
3 | */
4 | +function ($) { 'use strict'
5 | var HasMany = function (el, options) {
6 | this.$el = $(el)
7 | this.options = options
8 |
9 | this.alias = this.$el.data('alias')
10 | this.config = this.$el.data('config')
11 | this.$listContainer = this.$el.find('[data-control="list-container"]')
12 | this.$list = this.$el.find('[data-control="list"]')
13 | this.$addItem = this.$el.find('[data-control="add-model"]')
14 | this.$template = this.$el.find('[data-control="template"]')
15 | this.$deleteBox = this.$el.find('[data-control="delete-box"]')
16 |
17 | this.init()
18 | }
19 |
20 | /**
21 | * Initialize widget
22 | */
23 | HasMany.prototype.init = function() {
24 | var self = this
25 |
26 | // Attach sortable
27 | if (this.config.sortable) {
28 | this.$list.sortable({
29 | animation: 350,
30 | handle: '.sort-handle',
31 | onStart: function () {
32 | self.$list.addClass('sorting')
33 | },
34 | onEnd: function () {
35 | self.$list.removeClass('sorting')
36 | },
37 | })
38 | }
39 |
40 | // Attach item and delete click handlers
41 | this.$list.unbind().on('click', '[data-control="delete"]', function() {
42 | self.deleteItem($(this).closest('li'))
43 | return false
44 | }).on('click', 'li[data-control="item"]', function() {
45 | self.popup($(this))
46 | return false
47 | })
48 |
49 | // Handle add-model clicks
50 | this.$addItem.unbind().on('click', function() {
51 | var $item = self.addItem()
52 | self.popup($item, true)
53 | return false
54 | })
55 | }
56 |
57 | /**
58 | * Gets a model ID from a list item
59 | * @param
60 | * @return integer
61 | */
62 | HasMany.prototype.getModelId = function ($item) {
63 | var model = {}
64 | if (model = $item.find('[data-control="model"]').val()) {
65 | model = JSON.parse(model)
66 | if (typeof model.id != 'undefined')
67 | return model.id
68 | }
69 | return 0
70 | }
71 |
72 | /**
73 | * Append a new list item, and pass to popup()
74 | */
75 | HasMany.prototype.addItem = function() {
76 | this.$list.append(' ')
77 | return this.$list.find('li').last()
78 | }
79 |
80 | /*
81 | * Delete a list item
82 | * @param $item
83 | */
84 | HasMany.prototype.deleteItem = function($item) {
85 | var self = this,
86 | modelId = this.getModelId($item),
87 |
88 | // Saved records are deferred, unsaved records are deleted immediately
89 | confirmationMsg = modelId !== 0
90 | ? 'Delete this record upon saving?'
91 | : 'Delete this unsaved record?',
92 | successMsg = modelId !== 0
93 | ? 'The record will be deleted upon saving.'
94 | : 'The record has been deleted.'
95 |
96 | $item.addClass('delete-highlight')
97 |
98 | sweetAlert({
99 | title: confirmationMsg,
100 | showCancelButton: true,
101 | confirmButtonText: 'Confirm',
102 | }, function(isConfirm){
103 | if (isConfirm) {
104 | $.oc.flashMsg({
105 | text: successMsg,
106 | 'class': 'success',
107 | 'interval': 3
108 | })
109 | $item.slideUp('fast', function() {
110 | $(this).remove()
111 | if (modelId) {
112 | var deleteJson = JSON.parse(self.$deleteBox.val())
113 | deleteJson.push(modelId)
114 | self.$deleteBox.val(JSON.stringify(deleteJson))
115 | }
116 | })
117 | } else {
118 | $item.removeClass('delete-highlight')
119 | }
120 | })
121 | }
122 |
123 | /**
124 | * Open a popup form
125 | */
126 | HasMany.prototype.popup = function($item, newModel) {
127 | var self = this,
128 | model = {},
129 | modelId = this.getModelId($item)
130 |
131 | if (model = $item.find('[data-control="model"]').val())
132 | model = JSON.parse(model)
133 |
134 | this.itemSaved = false
135 |
136 | // Popup opened
137 | $item.one('show.oc.popup', function(e) {
138 | self.openPopup(e, modelId, $item)
139 | return false
140 | })
141 |
142 | // Popup closed
143 | $item.one('hide.oc.popup', function() {
144 | self.hidePopup($item, newModel)
145 | return false
146 | })
147 |
148 | // Popup settings
149 | var popSettings = {
150 | placement: 'center', modal: true,
151 | closeOnPageClick: true,
152 | highlightModelTarget: true,
153 | useAnimation: true,
154 | width: 600
155 | }
156 |
157 | // If this is a new model, create a popup from the template. Otherwise,
158 | // make an ajax request to render the popup with the supplied model data
159 | if (newModel) {
160 | $item.popup({
161 | content: this.$template.html().replace(new RegExp('script>', 'g'), 'script>'),
162 | placement: popSettings.placement,
163 | modal: popSettings.modal,
164 | closeOnPageClick: popSettings.closeOnPageClick,
165 | highlightModelTarget: popSettings.highlightModelTarget,
166 | useAnimation: popSettings.useAnimation,
167 | width: popSettings.width
168 | })
169 | } else {
170 | $item.popup({
171 | extraData: {':model': model},
172 | handler: this.alias + '::onRenderForm',
173 | placement: popSettings.placement,
174 | modal: popSettings.modal,
175 | closeOnPageClick: popSettings.closeOnPageClick,
176 | highlightModelTarget: popSettings.highlightModelTarget,
177 | useAnimation: popSettings.useAnimation,
178 | width: popSettings.width
179 | })
180 | }
181 |
182 | return false
183 | }
184 |
185 | /**
186 | * Handles popup opened events
187 | * @param event e
188 | * @param integer modelId
189 | * @param $item
190 | */
191 | HasMany.prototype.openPopup = function(e, modelId, $item) {
192 | var self = this
193 |
194 | // Capture the container and form
195 | self.$popupContainer = $(e.relatedTarget)
196 | self.$popupForm = self.$popupContainer.find('form')
197 |
198 | $(document).trigger('render')
199 |
200 | // Attach a save handler to the "apply" button
201 | $('button[data-control="apply-btn"]', self.$popupContainer).on('click', function() {
202 | self.applyChanges(modelId, $item)
203 | return false
204 | })
205 |
206 | // Fire opened event
207 | $(document).trigger('owl.hasmany.opened', {
208 | alias: self.alias,
209 | item: $item,
210 | form: self.$popupForm
211 | })
212 | }
213 |
214 | /**
215 | * Apply changes to an item form
216 | * @param integer modelId
217 | * @param $item
218 | */
219 | HasMany.prototype.applyChanges = function(modelId, $item) {
220 | var self = this
221 | var $loadingIndicator = self.$popupContainer.find('[data-control="loading-indicator"]'),
222 | $modalFooter = self.$popupContainer.find('.modal-footer')
223 |
224 | $modalFooter.addClass('in-progress')
225 | $loadingIndicator.loadIndicator({
226 | text: 'Applying...'
227 | })
228 |
229 | self.$popupForm.request(self.alias + '::onProcessForm', {
230 | data: {
231 | id: modelId
232 | },
233 | success: function(data) {
234 | self.itemSaved = true
235 | $item.html(data.item)
236 | self.$popupContainer.trigger('close.oc.popup')
237 | self.$el.trigger('change')
238 | },
239 | complete: function() {
240 | $modalFooter.removeClass('in-progress')
241 | $loadingIndicator.loadIndicator('hide')
242 | }
243 | })
244 | }
245 |
246 | /**
247 | * Handles popup closed events
248 | */
249 | HasMany.prototype.hidePopup = function($item, newModel) {
250 | var self = this
251 |
252 | // Remove new un-saved items
253 | if (newModel && !self.itemSaved)
254 | $item.remove()
255 |
256 | $item.css('display', '')
257 |
258 | // Prevent dom pollution
259 | $('span[role="status"]').remove()
260 |
261 | // Fire closed event
262 | $(document).trigger('owl.hasmany.closed', {
263 | alias: self.alias,
264 | item: $item,
265 | form: self.$popupForm
266 | })
267 | }
268 |
269 | /*
270 | * Bind and construct non-conflicting hasmany
271 | */
272 | var old = $.fn.HasMany
273 |
274 | $.fn.HasMany = function (config) {
275 | return new HasMany($(this), config)
276 | }
277 |
278 | $.fn.HasMany.Constructor = HasMany
279 |
280 | $.fn.HasMany.noConflict = function () {
281 | $.fn.HasMany = old
282 | return this
283 | }
284 |
285 | }(window.jQuery)
286 |
--------------------------------------------------------------------------------
/src/FormWidgets/Knob/widget/assets/js/widget.js:
--------------------------------------------------------------------------------
1 | (function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e){"use strict";var t={},n=Math.max,r=Math.min;t.c={};t.c.d=e(document);t.c.t=function(e){return e.originalEvent.touches.length-1};t.o=function(){var n=this;this.o=null;this.$=null;this.i=null;this.g=null;this.v=null;this.cv=null;this.x=0;this.y=0;this.w=0;this.h=0;this.$c=null;this.c=null;this.t=0;this.isInit=false;this.fgColor=null;this.pColor=null;this.dH=null;this.cH=null;this.eH=null;this.rH=null;this.scale=1;this.relative=false;this.relativeWidth=false;this.relativeHeight=false;this.$div=null;this.run=function(){var t=function(e,t){var r;for(r in t){n.o[r]=t[r]}n._carve().init();n._configure()._draw()};if(this.$.data("kontroled"))return;this.$.data("kontroled",true);this.extend();this.o=e.extend({min:this.$.data("min")!==undefined?this.$.data("min"):0,max:this.$.data("max")!==undefined?this.$.data("max"):100,stopper:true,readOnly:this.$.data("readonly")||this.$.attr("readonly")==="readonly",cursor:this.$.data("cursor")===true&&30||this.$.data("cursor")||0,thickness:this.$.data("thickness")&&Math.max(Math.min(this.$.data("thickness"),1),.01)||.35,lineCap:this.$.data("linecap")||"butt",width:this.$.data("width")||200,height:this.$.data("height")||200,displayInput:this.$.data("displayinput")==null||this.$.data("displayinput"),displayPrevious:this.$.data("displayprevious"),fgColor:this.$.data("fgcolor")||"#87CEEB",inputColor:this.$.data("inputcolor"),font:this.$.data("font")||"Arial",fontWeight:this.$.data("font-weight")||"bold",inline:false,step:this.$.data("step")||1,rotation:this.$.data("rotation"),draw:null,change:null,cancel:null,release:null,format:function(e){return e},parse:function(e){return parseFloat(e)}},this.o);this.o.flip=this.o.rotation==="anticlockwise"||this.o.rotation==="acw";if(!this.o.inputColor){this.o.inputColor=this.o.fgColor}if(this.$.is("fieldset")){this.v={};this.i=this.$.find("input");this.i.each(function(t){var r=e(this);n.i[t]=r;n.v[t]=n.o.parse(r.val());r.bind("change blur",function(){var e={};e[t]=r.val();n.val(n._validate(e))})});this.$.find("legend").remove()}else{this.i=this.$;this.v=this.o.parse(this.$.val());this.v===""&&(this.v=this.o.min);this.$.bind("change blur",function(){n.val(n._validate(n.o.parse(n.$.val())))})}!this.o.displayInput&&this.$.hide();this.$c=e(document.createElement("canvas")).attr({width:this.o.width,height:this.o.height});this.$div=e('
');this.$.wrap(this.$div).before(this.$c);this.$div=this.$.parent();if(typeof G_vmlCanvasManager!=="undefined"){G_vmlCanvasManager.initElement(this.$c[0])}this.c=this.$c[0].getContext?this.$c[0].getContext("2d"):null;if(!this.c){throw{name:"CanvasNotSupportedException",message:"Canvas not supported. Please use excanvas on IE8.0.",toString:function(){return this.name+": "+this.message}}}this.scale=(window.devicePixelRatio||1)/(this.c.webkitBackingStorePixelRatio||this.c.mozBackingStorePixelRatio||this.c.msBackingStorePixelRatio||this.c.oBackingStorePixelRatio||this.c.backingStorePixelRatio||1);this.relativeWidth=this.o.width%1!==0&&this.o.width.indexOf("%");this.relativeHeight=this.o.height%1!==0&&this.o.height.indexOf("%");this.relative=this.relativeWidth||this.relativeHeight;this._carve();if(this.v instanceof Object){this.cv={};this.copy(this.v,this.cv)}else{this.cv=this.v}this.$.bind("configure",t).parent().bind("configure",t);this._listen()._configure()._xy().init();this.isInit=true;this.$.val(this.o.format(this.v));this._draw();return this};this._carve=function(){if(this.relative){var e=this.relativeWidth?this.$div.parent().width()*parseInt(this.o.width)/100:this.$div.parent().width(),t=this.relativeHeight?this.$div.parent().height()*parseInt(this.o.height)/100:this.$div.parent().height();this.w=this.h=Math.min(e,t)}else{this.w=this.o.width;this.h=this.o.height}this.$div.css({width:this.w+"px",height:this.h+"px"});this.$c.attr({width:this.w,height:this.h});if(this.scale!==1){this.$c[0].width=this.$c[0].width*this.scale;this.$c[0].height=this.$c[0].height*this.scale;this.$c.width(this.w);this.$c.height(this.h)}return this};this._draw=function(){var e=true;n.g=n.c;n.clear();n.dH&&(e=n.dH());e!==false&&n.draw()};this._touch=function(e){var r=function(e){var t=n.xy2val(e.originalEvent.touches[n.t].pageX,e.originalEvent.touches[n.t].pageY);if(t==n.cv)return;if(n.cH&&n.cH(t)===false)return;n.change(n._validate(t));n._draw()};this.t=t.c.t(e);r(e);t.c.d.bind("touchmove.k",r).bind("touchend.k",function(){t.c.d.unbind("touchmove.k touchend.k");n.val(n.cv)});return this};this._mouse=function(e){var r=function(e){var t=n.xy2val(e.pageX,e.pageY);if(t==n.cv)return;if(n.cH&&n.cH(t)===false)return;n.change(n._validate(t));n._draw()};r(e);t.c.d.bind("mousemove.k",r).bind("keyup.k",function(e){if(e.keyCode===27){t.c.d.unbind("mouseup.k mousemove.k keyup.k");if(n.eH&&n.eH()===false)return;n.cancel()}}).bind("mouseup.k",function(e){t.c.d.unbind("mousemove.k mouseup.k keyup.k");n.val(n.cv)});return this};this._xy=function(){var e=this.$c.offset();this.x=e.left;this.y=e.top;return this};this._listen=function(){if(!this.o.readOnly){this.$c.bind("mousedown",function(e){e.preventDefault();n._xy()._mouse(e)}).bind("touchstart",function(e){e.preventDefault();n._xy()._touch(e)});this.listen()}else{this.$.attr("readonly","readonly")}if(this.relative){e(window).resize(function(){n._carve().init();n._draw()})}return this};this._configure=function(){if(this.o.draw)this.dH=this.o.draw;if(this.o.change)this.cH=this.o.change;if(this.o.cancel)this.eH=this.o.cancel;if(this.o.release)this.rH=this.o.release;if(this.o.displayPrevious){this.pColor=this.h2rgba(this.o.fgColor,"0.4");this.fgColor=this.h2rgba(this.o.fgColor,"0.6")}else{this.fgColor=this.o.fgColor}return this};this._clear=function(){this.$c[0].width=this.$c[0].width};this._validate=function(e){var t=~~((e<0?-.5:.5)+e/this.o.step)*this.o.step;return Math.round(t*100)/100};this.listen=function(){};this.extend=function(){};this.init=function(){};this.change=function(e){};this.val=function(e){};this.xy2val=function(e,t){};this.draw=function(){};this.clear=function(){this._clear()};this.h2rgba=function(e,t){var n;e=e.substring(1,7);n=[parseInt(e.substring(0,2),16),parseInt(e.substring(2,4),16),parseInt(e.substring(4,6),16)];return"rgba("+n[0]+","+n[1]+","+n[2]+","+t+")"};this.copy=function(e,t){for(var n in e){t[n]=e[n]}}};t.Dial=function(){t.o.call(this);this.startAngle=null;this.xy=null;this.radius=null;this.lineWidth=null;this.cursorExt=null;this.w2=null;this.PI2=2*Math.PI;this.extend=function(){this.o=e.extend({bgColor:this.$.data("bgcolor")||"#EEEEEE",angleOffset:this.$.data("angleoffset")||0,angleArc:this.$.data("anglearc")||360,inline:true},this.o)};this.val=function(e,t){if(null!=e){e=this.o.parse(e);if(t!==false&&e!=this.v&&this.rH&&this.rH(e)===false){return}this.cv=this.o.stopper?n(r(e,this.o.max),this.o.min):e;this.v=this.cv;this.$.val(this.o.format(this.v));this._draw()}else{return this.v}};this.xy2val=function(e,t){var i,s;i=Math.atan2(e-(this.x+this.w2),-(t-this.y-this.w2))-this.angleOffset;if(this.o.flip){i=this.angleArc-i-this.PI2}if(this.angleArc!=this.PI2&&i<0&&i>-.5){i=0}else if(i<0){i+=this.PI2}s=i*(this.o.max-this.o.min)/this.angleArc+this.o.min;this.o.stopper&&(s=n(r(s,this.o.max),this.o.min));return s};this.listen=function(){var t=this,i,s,o=function(e){e.preventDefault();var o=e.originalEvent,u=o.detail||o.wheelDeltaX,a=o.detail||o.wheelDeltaY,f=t._validate(t.o.parse(t.$.val()))+(u>0||a>0?t.o.step:u<0||a<0?-t.o.step:0);f=n(r(f,t.o.max),t.o.min);t.val(f,false);if(t.rH){clearTimeout(i);i=setTimeout(function(){t.rH(f);i=null},100);if(!s){s=setTimeout(function(){if(i)t.rH(f);s=null},200)}}},u,a,f=1,l={37:-t.o.step,38:t.o.step,39:t.o.step,40:-t.o.step};this.$.bind("keydown",function(i){var s=i.keyCode;if(s>=96&&s<=105){s=i.keyCode=s-48}u=parseInt(String.fromCharCode(s));if(isNaN(u)){s!==13&&s!==8&&s!==9&&s!==189&&(s!==190||t.$.val().match(/\./))&&i.preventDefault();if(e.inArray(s,[37,38,39,40])>-1){i.preventDefault();var o=t.o.parse(t.$.val())+l[s]*f;t.o.stopper&&(o=n(r(o,t.o.max),t.o.min));t.change(t._validate(o));t._draw();a=window.setTimeout(function(){f*=2},30)}}}).bind("keyup",function(e){if(isNaN(u)){if(a){window.clearTimeout(a);a=null;f=1;t.val(t.$.val())}}else{t.$.val()>t.o.max&&t.$.val(t.o.max)||t.$.val()this.o.max){this.v=this.o.min}this.$.val(this.v);this.w2=this.w/2;this.cursorExt=this.o.cursor/100;this.xy=this.w2*this.scale;this.lineWidth=this.xy*this.o.thickness;this.lineCap=this.o.lineCap;this.radius=this.xy-this.lineWidth/2;this.o.angleOffset&&(this.o.angleOffset=isNaN(this.o.angleOffset)?0:this.o.angleOffset);this.o.angleArc&&(this.o.angleArc=isNaN(this.o.angleArc)?this.PI2:this.o.angleArc);this.angleOffset=this.o.angleOffset*Math.PI/180;this.angleArc=this.o.angleArc*Math.PI/180;this.startAngle=1.5*Math.PI+this.angleOffset;this.endAngle=1.5*Math.PI+this.angleOffset+this.angleArc;var e=n(String(Math.abs(this.o.max)).length,String(Math.abs(this.o.min)).length,2)+2;this.o.displayInput&&this.i.css({width:(this.w/2+4>>0)+"px",height:(this.w/3>>0)+"px",position:"absolute","vertical-align":"middle","margin-top":(this.w/3>>0)+"px","margin-left":"-"+(this.w*3/4+2>>0)+"px",border:0,background:"none",font:this.o.fontWeight+" "+(this.w/e>>0)+"px "+this.o.font,"text-align":"center",color:this.o.inputColor||this.o.fgColor,padding:"0px","-webkit-appearance":"none"})||this.i.css({width:"0px",visibility:"hidden"})};this.change=function(e){this.cv=e;this.$.val(this.o.format(e))};this.angle=function(e){return(e-this.o.min)*this.angleArc/(this.o.max-this.o.min)};this.arc=function(e){var t,n;e=this.angle(e);if(this.o.flip){t=this.endAngle+1e-5;n=t-e-1e-5}else{t=this.startAngle-1e-5;n=t+e+1e-5}this.o.cursor&&(t=n-this.cursorExt)&&(n=n+this.cursorExt);return{s:t,e:n,d:this.o.flip&&!this.o.cursor}};this.draw=function(){var e=this.g,t=this.arc(this.cv),n,r=1;e.lineWidth=this.lineWidth;e.lineCap=this.lineCap;if(this.o.bgColor!=="none"){e.beginPath();e.strokeStyle=this.o.bgColor;e.arc(this.xy,this.xy,this.radius,this.endAngle-1e-5,this.startAngle+1e-5,true);e.stroke()}if(this.o.displayPrevious){n=this.arc(this.v);e.beginPath();e.strokeStyle=this.pColor;e.arc(this.xy,this.xy,this.radius,n.s,n.e,n.d);e.stroke();r=this.cv==this.v}e.beginPath();e.strokeStyle=r?this.o.fgColor:this.fgColor;e.arc(this.xy,this.xy,this.radius,t.s,t.e,t.d);e.stroke()};this.cancel=function(){this.val(this.v)}};e.fn.dial=e.fn.knob=function(n){return this.each(function(){var r=new t.Dial;r.o=n;r.$=e(this);r.run()}).parent()}})
2 |
--------------------------------------------------------------------------------
/src/FormWidgets/Money/widget/assets/js/jquery.maskMoney.js:
--------------------------------------------------------------------------------
1 | (function ($) {
2 | "use strict";
3 | if (!$.browser) {
4 | $.browser = {};
5 | $.browser.mozilla = /mozilla/.test(navigator.userAgent.toLowerCase()) && !/webkit/.test(navigator.userAgent.toLowerCase());
6 | $.browser.webkit = /webkit/.test(navigator.userAgent.toLowerCase());
7 | $.browser.opera = /opera/.test(navigator.userAgent.toLowerCase());
8 | $.browser.msie = /msie/.test(navigator.userAgent.toLowerCase());
9 | }
10 |
11 | var methods = {
12 | destroy : function () {
13 | $(this).unbind(".maskMoney");
14 |
15 | if ($.browser.msie) {
16 | this.onpaste = null;
17 | }
18 | return this;
19 | },
20 |
21 | mask : function (value) {
22 | return this.each(function () {
23 | var $this = $(this),
24 | decimalSize;
25 | if (typeof value === "number") {
26 | $this.trigger("mask");
27 | decimalSize = $($this.val().split(/\D/)).last()[0].length;
28 | value = value.toFixed(decimalSize);
29 | $this.val(value);
30 | }
31 | return $this.trigger("mask");
32 | });
33 | },
34 |
35 | unmasked : function () {
36 | return this.map(function () {
37 | var value = ($(this).val() || "0"),
38 | isNegative = value.indexOf("-") !== -1,
39 | decimalPart;
40 | // get the last position of the array that is a number(coercion makes "" to be evaluated as false)
41 | $(value.split(/\D/).reverse()).each(function (index, element) {
42 | if(element) {
43 | decimalPart = element;
44 | return false;
45 | }
46 | });
47 | value = value.replace(/\D/g, "");
48 | value = value.replace(new RegExp(decimalPart + "$"), "." + decimalPart);
49 | if (isNegative) {
50 | value = "-" + value;
51 | }
52 | return parseFloat(value);
53 | });
54 | },
55 |
56 | init : function (settings) {
57 | settings = $.extend({
58 | prefix: "",
59 | suffix: "",
60 | affixesStay: true,
61 | thousands: ",",
62 | decimal: ".",
63 | precision: 2,
64 | allowZero: false,
65 | allowNegative: false
66 | }, settings);
67 |
68 | return this.each(function () {
69 | var $input = $(this),
70 | onFocusValue;
71 |
72 | // data-* api
73 | settings = $.extend(settings, $input.data());
74 |
75 | function getInputSelection() {
76 | var el = $input.get(0),
77 | start = 0,
78 | end = 0,
79 | normalizedValue,
80 | range,
81 | textInputRange,
82 | len,
83 | endRange;
84 |
85 | if (typeof el.selectionStart === "number" && typeof el.selectionEnd === "number") {
86 | start = el.selectionStart;
87 | end = el.selectionEnd;
88 | } else {
89 | range = document.selection.createRange();
90 |
91 | if (range && range.parentElement() === el) {
92 | len = el.value.length;
93 | normalizedValue = el.value.replace(/\r\n/g, "\n");
94 |
95 | // Create a working TextRange that lives only in the input
96 | textInputRange = el.createTextRange();
97 | textInputRange.moveToBookmark(range.getBookmark());
98 |
99 | // Check if the start and end of the selection are at the very end
100 | // of the input, since moveStart/moveEnd doesn't return what we want
101 | // in those cases
102 | endRange = el.createTextRange();
103 | endRange.collapse(false);
104 |
105 | if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
106 | start = end = len;
107 | } else {
108 | start = -textInputRange.moveStart("character", -len);
109 | start += normalizedValue.slice(0, start).split("\n").length - 1;
110 |
111 | if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
112 | end = len;
113 | } else {
114 | end = -textInputRange.moveEnd("character", -len);
115 | end += normalizedValue.slice(0, end).split("\n").length - 1;
116 | }
117 | }
118 | }
119 | }
120 |
121 | return {
122 | start: start,
123 | end: end
124 | };
125 | } // getInputSelection
126 |
127 | function canInputMoreNumbers() {
128 | var haventReachedMaxLength = !($input.val().length >= $input.attr("maxlength") && $input.attr("maxlength") >= 0),
129 | selection = getInputSelection(),
130 | start = selection.start,
131 | end = selection.end,
132 | haveNumberSelected = (selection.start !== selection.end && $input.val().substring(start, end).match(/\d/)) ? true : false,
133 | startWithZero = ($input.val().substring(0, 1) === "0");
134 | return haventReachedMaxLength || haveNumberSelected || startWithZero;
135 | }
136 |
137 | function setCursorPosition(pos) {
138 | $input.each(function (index, elem) {
139 | if (elem.setSelectionRange) {
140 | elem.focus();
141 | elem.setSelectionRange(pos, pos);
142 | } else if (elem.createTextRange) {
143 | var range = elem.createTextRange();
144 | range.collapse(true);
145 | range.moveEnd("character", pos);
146 | range.moveStart("character", pos);
147 | range.select();
148 | }
149 | });
150 | }
151 |
152 | function setSymbol(value) {
153 | var operator = "";
154 | if (value.indexOf("-") > -1) {
155 | value = value.replace("-", "");
156 | operator = "-";
157 | }
158 | return operator + settings.prefix + value + settings.suffix;
159 | }
160 |
161 | function maskValue(value) {
162 | var negative = (value.indexOf("-") > -1 && settings.allowNegative) ? "-" : "",
163 | onlyNumbers = value.replace(/[^0-9]/g, ""),
164 | integerPart = onlyNumbers.slice(0, onlyNumbers.length - settings.precision),
165 | newValue,
166 | decimalPart,
167 | leadingZeros;
168 |
169 | // remove initial zeros
170 | integerPart = integerPart.replace(/^0*/g, "");
171 | // put settings.thousands every 3 chars
172 | integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, settings.thousands);
173 | if (integerPart === "") {
174 | integerPart = "0";
175 | }
176 | newValue = negative + integerPart;
177 |
178 | if (settings.precision > 0) {
179 | decimalPart = onlyNumbers.slice(onlyNumbers.length - settings.precision);
180 | leadingZeros = new Array((settings.precision + 1) - decimalPart.length).join(0);
181 | newValue += settings.decimal + leadingZeros + decimalPart;
182 | }
183 | return setSymbol(newValue);
184 | }
185 |
186 | function maskAndPosition(startPos) {
187 | var originalLen = $input.val().length,
188 | newLen;
189 | $input.val(maskValue($input.val()));
190 | newLen = $input.val().length;
191 | startPos = startPos - (originalLen - newLen);
192 | setCursorPosition(startPos);
193 | }
194 |
195 | function mask() {
196 | var value = $input.val();
197 | $input.val(maskValue(value));
198 | }
199 |
200 | function changeSign() {
201 | var inputValue = $input.val();
202 | if (settings.allowNegative) {
203 | if (inputValue !== "" && inputValue.charAt(0) === "-") {
204 | return inputValue.replace("-", "");
205 | } else {
206 | return "-" + inputValue;
207 | }
208 | } else {
209 | return inputValue;
210 | }
211 | }
212 |
213 | function preventDefault(e) {
214 | if (e.preventDefault) { //standard browsers
215 | e.preventDefault();
216 | } else { // old internet explorer
217 | e.returnValue = false;
218 | }
219 | }
220 |
221 | function keypressEvent(e) {
222 | e = e || window.event;
223 | var key = e.which || e.charCode || e.keyCode,
224 | keyPressedChar,
225 | selection,
226 | startPos,
227 | endPos,
228 | value;
229 | //added to handle an IE "special" event
230 | if (key === undefined) {
231 | return false;
232 | }
233 |
234 | // any key except the numbers 0-9
235 | if (key < 48 || key > 57) {
236 | // -(minus) key
237 | if (key === 45) {
238 | $input.val(changeSign());
239 | return false;
240 | // +(plus) key
241 | } else if (key === 43) {
242 | $input.val($input.val().replace("-", ""));
243 | return false;
244 | // enter key or tab key
245 | } else if (key === 13 || key === 9) {
246 | return true;
247 | } else if ($.browser.mozilla && (key === 37 || key === 39) && e.charCode === 0) {
248 | // needed for left arrow key or right arrow key with firefox
249 | // the charCode part is to avoid allowing "%"(e.charCode 0, e.keyCode 37)
250 | return true;
251 | } else { // any other key with keycode less than 48 and greater than 57
252 | preventDefault(e);
253 | return true;
254 | }
255 | } else if (!canInputMoreNumbers()) {
256 | return false;
257 | } else {
258 | preventDefault(e);
259 |
260 | keyPressedChar = String.fromCharCode(key);
261 | selection = getInputSelection();
262 | startPos = selection.start;
263 | endPos = selection.end;
264 | value = $input.val();
265 | $input.val(value.substring(0, startPos) + keyPressedChar + value.substring(endPos, value.length));
266 | maskAndPosition(startPos + 1);
267 | return false;
268 | }
269 | }
270 |
271 | function keydownEvent(e) {
272 | e = e || window.event;
273 | var key = e.which || e.charCode || e.keyCode,
274 | selection,
275 | startPos,
276 | endPos,
277 | value,
278 | lastNumber;
279 | //needed to handle an IE "special" event
280 | if (key === undefined) {
281 | return false;
282 | }
283 |
284 | selection = getInputSelection();
285 | startPos = selection.start;
286 | endPos = selection.end;
287 |
288 | if (key === 8 || key === 46 || key === 63272) { // backspace or delete key (with special case for safari)
289 | preventDefault(e);
290 |
291 | value = $input.val();
292 | // not a selection
293 | if (startPos === endPos) {
294 | // backspace
295 | if (key === 8) {
296 | if (settings.suffix === "") {
297 | startPos -= 1;
298 | } else {
299 | // needed to find the position of the last number to be erased
300 | lastNumber = value.split("").reverse().join("").search(/\d/);
301 | startPos = value.length - lastNumber - 1;
302 | endPos = startPos + 1;
303 | }
304 | //delete
305 | } else {
306 | endPos += 1;
307 | }
308 | }
309 |
310 | $input.val(value.substring(0, startPos) + value.substring(endPos, value.length));
311 |
312 | maskAndPosition(startPos);
313 | return false;
314 | } else if (key === 9) { // tab key
315 | return true;
316 | } else { // any other key
317 | return true;
318 | }
319 | }
320 |
321 | function focusEvent() {
322 | onFocusValue = $input.val();
323 | mask();
324 | var input = $input.get(0),
325 | textRange;
326 | if (input.createTextRange) {
327 | textRange = input.createTextRange();
328 | textRange.collapse(false); // set the cursor at the end of the input
329 | textRange.select();
330 | }
331 | }
332 |
333 | function cutPasteEvent() {
334 | setTimeout(function() {
335 | mask();
336 | }, 0);
337 | }
338 |
339 | function getDefaultMask() {
340 | var n = parseFloat("0") / Math.pow(10, settings.precision);
341 | return (n.toFixed(settings.precision)).replace(new RegExp("\\.", "g"), settings.decimal);
342 | }
343 |
344 | function blurEvent(e) {
345 | if ($.browser.msie) {
346 | keypressEvent(e);
347 | }
348 |
349 | if ($input.val() === "" || $input.val() === setSymbol(getDefaultMask())) {
350 | if (!settings.allowZero) {
351 | $input.val("");
352 | } else if (!settings.affixesStay) {
353 | $input.val(getDefaultMask());
354 | } else {
355 | $input.val(setSymbol(getDefaultMask()));
356 | }
357 | } else {
358 | if (!settings.affixesStay) {
359 | var newValue = $input.val().replace(settings.prefix, "").replace(settings.suffix, "");
360 | $input.val(newValue);
361 | }
362 | }
363 | if ($input.val() !== onFocusValue) {
364 | $input.change();
365 | }
366 | }
367 |
368 | function clickEvent() {
369 | var input = $input.get(0),
370 | length;
371 | if (input.setSelectionRange) {
372 | length = $input.val().length;
373 | input.setSelectionRange(length, length);
374 | } else {
375 | $input.val($input.val());
376 | }
377 | }
378 |
379 | $input.unbind(".maskMoney");
380 | $input.bind("keypress.maskMoney", keypressEvent);
381 | $input.bind("keydown.maskMoney", keydownEvent);
382 | $input.bind("blur.maskMoney", blurEvent);
383 | $input.bind("focus.maskMoney", focusEvent);
384 | $input.bind("click.maskMoney", clickEvent);
385 | $input.bind("cut.maskMoney", cutPasteEvent);
386 | $input.bind("paste.maskMoney", cutPasteEvent);
387 | $input.bind("mask.maskMoney", mask);
388 | });
389 | }
390 | };
391 |
392 | $.fn.maskMoney = function (method) {
393 | if (methods[method]) {
394 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
395 | } else if (typeof method === "object" || ! method) {
396 | return methods.init.apply(this, arguments);
397 | } else {
398 | $.error("Method " + method + " does not exist on jQuery.maskMoney");
399 | }
400 | };
401 | })(window.jQuery || window.Zepto);
402 |
--------------------------------------------------------------------------------