Shortcake (Shortcode UI) - Version 0.3.0

Version Description

= 0.3 =

We've removed the compatibility shim for the magical content attribute. If you were using this to support editing inner content, you'll need to change your UI registration to use inner_content.

Download this release

Release Info

Developer danielbachhuber
Plugin Icon 128x128 Shortcake (Shortcode UI)
Version 0.3.0
Comparing to
See all releases

Code changes from version 0.2.3 to 0.3.0

Files changed (53) hide show
  1. Gruntfile.js +59 -34
  2. composer.json +9 -1
  3. css/sass/shortcode-ui-editor-styles.scss +4 -38
  4. css/shortcode-ui-editor-styles.css +9 -36
  5. css/shortcode-ui-editor-styles.css.map +1 -1
  6. inc/class-shortcode-ui.php +55 -62
  7. inc/fields/class-field-attachment.php +6 -6
  8. inc/fields/class-field-color.php +6 -5
  9. inc/fields/class-field-post-select.php +166 -0
  10. inc/fields/class-shortcode-ui-fields.php +11 -2
  11. inc/templates/edit-form.tpl.php +24 -21
  12. js/build/field-attachment.js +65 -20
  13. js/build/field-color.js +60 -15
  14. js/build/field-post-select.js +202 -0
  15. js/build/shortcode-ui.js +169 -78
  16. js/{collections → src/collections}/shortcode-attributes.js +0 -0
  17. js/{collections → src/collections}/shortcodes.js +0 -0
  18. js/{controllers → src/controllers}/media-controller.js +2 -1
  19. js/{field-attachment.js → src/field-attachment.js} +7 -7
  20. js/{field-color.js → src/field-color.js} +2 -2
  21. js/src/field-post-select.js +199 -0
  22. js/{models → src/models}/inner-content.js +6 -1
  23. js/{models → src/models}/shortcode-attribute.js +4 -1
  24. js/{models → src/models}/shortcode.js +7 -6
  25. js/{shortcode-ui.js → src/shortcode-ui.js} +3 -3
  26. js/{utils → src/utils}/shortcode-view-constructor.js +43 -38
  27. js/{utils → src/utils}/sui.js +0 -0
  28. js/{views → src/views}/edit-attribute-field.js +41 -5
  29. js/{views → src/views}/edit-shortcode-form.js +24 -3
  30. js/{views → src/views}/insert-shortcode-list-item.js +0 -0
  31. js/{views → src/views}/insert-shortcode-list.js +0 -0
  32. js/{views → src/views}/media-frame.js +19 -5
  33. js/{views → src/views}/media-toolbar.js +0 -0
  34. js/{views → src/views}/search-shortcode.js +0 -0
  35. js/{views → src/views}/shortcode-preview.js +14 -10
  36. js/{views → src/views}/shortcode-ui.js +3 -3
  37. js/{views → src/views}/tabbed-view.js +2 -1
  38. languages/shortcode-ui-nl_NL.mo +0 -0
  39. languages/shortcode-ui-nl_NL.po +95 -0
  40. languages/shortcode-ui.pot +21 -13
  41. lib/select2/select2-spinner.gif +0 -0
  42. lib/select2/select2.css +704 -0
  43. lib/select2/select2.js +3541 -0
  44. lib/select2/select2.min.js +23 -0
  45. lib/select2/select2.png +0 -0
  46. lib/select2/select2x2.png +0 -0
  47. package.json +2 -0
  48. readme.txt +33 -5
  49. screenshot-1.png +0 -0
  50. screenshot-2.png +0 -0
  51. screenshot-3.png +0 -0
  52. screenshot-4.png +0 -0
  53. shortcode-ui.php +4 -2
Gruntfile.js CHANGED
@@ -33,7 +33,7 @@ module.exports = function( grunt ) {
33
  },
34
 
35
  scripts: {
36
- files: ['js/**/*.js', 'js-tests/src/**/*.js', '!js/build/**/*'],
37
  tasks: ['scripts'],
38
  options: {
39
  debounceDelay: 500,
@@ -44,6 +44,16 @@ module.exports = function( grunt ) {
44
 
45
  },
46
 
 
 
 
 
 
 
 
 
 
 
47
  browserify : {
48
 
49
  options: {
@@ -51,27 +61,27 @@ module.exports = function( grunt ) {
51
 
52
  b.plugin(remapify, [
53
  {
54
- cwd: 'js/models',
55
  src: '**/*.js',
56
  expose: 'sui-models'
57
  },
58
  {
59
- cwd: 'js/controllers',
60
  src: '**/*.js',
61
  expose: 'sui-controllers'
62
  },
63
  {
64
- cwd: 'js/collections',
65
  src: '**/*.js',
66
  expose: 'sui-collections'
67
  },
68
  {
69
- cwd: 'js/views',
70
  src: '**/*.js',
71
  expose: 'sui-views'
72
  },
73
  {
74
- cwd: 'js/utils',
75
  src: '**/*.js',
76
  expose: 'sui-utils'
77
  }
@@ -82,9 +92,10 @@ module.exports = function( grunt ) {
82
 
83
  dist: {
84
  files : {
85
- 'js/build/shortcode-ui.js' : ['js/shortcode-ui.js'],
86
- 'js/build/field-attachment.js' : ['js/field-attachment.js'],
87
- 'js/build/field-color.js' : ['js/field-color.js'],
 
88
  },
89
  options: {
90
  transform: ['browserify-shim']
@@ -121,43 +132,57 @@ module.exports = function( grunt ) {
121
  },
122
 
123
  addtextdomain: {
124
- options: {
125
- textdomain: 'shortcode-ui', // Project text domain.
126
- },
127
- target: {
128
- files: {
129
- src: [ '*.php', '**/*.php', '!node_modules/**', '!php-tests/**', '!bin/**' ]
130
- }
131
- }
132
  }, //addtextdomain
133
 
 
 
 
 
 
 
 
 
 
 
 
134
  makepot: {
135
- target: {
136
- options: {
137
- domainPath: '/languages',
138
- mainFile: 'shortcode-ui.php',
139
- potFilename: 'shortcode-ui.pot',
140
- potHeaders: {
141
- poedit: true,
142
- 'x-poedit-keywordslist': true
143
- },
144
- type: 'wp-plugin',
145
- updateTimestamp: true
146
- }
147
- }
148
  }, //makepot
149
  } );
150
 
151
  grunt.loadNpmTasks( 'grunt-sass' );
152
  grunt.loadNpmTasks( 'grunt-contrib-watch' );
153
  grunt.loadNpmTasks( 'grunt-browserify' );
154
- grunt.loadNpmTasks( 'grunt-wp-i18n' );
155
- grunt.loadNpmTasks( 'grunt-contrib-jasmine' );
 
 
156
 
157
- grunt.registerTask( 'scripts', [ 'browserify', 'jasmine' ] );
158
- grunt.registerTask( 'styles', [ 'sass' ] );
159
  grunt.registerTask( 'default', [ 'scripts', 'styles' ] );
160
  grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] );
 
161
 
162
  grunt.util.linefeed = '\n';
163
 
33
  },
34
 
35
  scripts: {
36
+ files: ['js/src/**/*.js', 'js-tests/src/**/*.js', '!js/build/**/*'],
37
  tasks: ['scripts'],
38
  options: {
39
  debounceDelay: 500,
44
 
45
  },
46
 
47
+ phpcs: {
48
+ plugin: {
49
+ src: './'
50
+ },
51
+ options: {
52
+ bin: "vendor/bin/phpcs --extensions=php --ignore=\"*/vendor/*,*/node_modules/*,dev.php\"",
53
+ standard: "phpcs.ruleset.xml"
54
+ }
55
+ },
56
+
57
  browserify : {
58
 
59
  options: {
61
 
62
  b.plugin(remapify, [
63
  {
64
+ cwd: 'js/src/models',
65
  src: '**/*.js',
66
  expose: 'sui-models'
67
  },
68
  {
69
+ cwd: 'js/src/controllers',
70
  src: '**/*.js',
71
  expose: 'sui-controllers'
72
  },
73
  {
74
+ cwd: 'js/src/collections',
75
  src: '**/*.js',
76
  expose: 'sui-collections'
77
  },
78
  {
79
+ cwd: 'js/src/views',
80
  src: '**/*.js',
81
  expose: 'sui-views'
82
  },
83
  {
84
+ cwd: 'js/src/utils',
85
  src: '**/*.js',
86
  expose: 'sui-utils'
87
  }
92
 
93
  dist: {
94
  files : {
95
+ 'js/build/shortcode-ui.js' : ['js/src/shortcode-ui.js'],
96
+ 'js/build/field-attachment.js' : ['js/src/field-attachment.js'],
97
+ 'js/build/field-color.js' : ['js/src/field-color.js'],
98
+ 'js/build/field-post-select.js' : ['js/src/field-post-select.js'],
99
  },
100
  options: {
101
  transform: ['browserify-shim']
132
  },
133
 
134
  addtextdomain: {
135
+ options: {
136
+ textdomain: 'shortcode-ui', // Project text domain.
137
+ },
138
+ target: {
139
+ files: {
140
+ src: [ '*.php', '**/*.php', '!node_modules/**', '!php-tests/**', '!bin/**' ]
141
+ }
142
+ }
143
  }, //addtextdomain
144
 
145
+ wp_readme_to_markdown: {
146
+ your_target: {
147
+ files: {
148
+ 'README.md': 'readme.txt'
149
+ },
150
+ options: {
151
+ screenshot_url: 'http://s.wordpress.org/extend/plugins/shortcode-ui/{screenshot}.png',
152
+ }
153
+ },
154
+ },
155
+
156
  makepot: {
157
+ target: {
158
+ options: {
159
+ domainPath: '/languages',
160
+ mainFile: 'shortcode-ui.php',
161
+ potFilename: 'shortcode-ui.pot',
162
+ potHeaders: {
163
+ poedit: true,
164
+ 'x-poedit-keywordslist': true
165
+ },
166
+ type: 'wp-plugin',
167
+ updateTimestamp: true
168
+ }
169
+ }
170
  }, //makepot
171
  } );
172
 
173
  grunt.loadNpmTasks( 'grunt-sass' );
174
  grunt.loadNpmTasks( 'grunt-contrib-watch' );
175
  grunt.loadNpmTasks( 'grunt-browserify' );
176
+ grunt.loadNpmTasks( 'grunt-phpcs' );
177
+ grunt.loadNpmTasks( 'grunt-wp-i18n' );
178
+ grunt.loadNpmTasks( 'grunt-wp-readme-to-markdown' );
179
+ grunt.loadNpmTasks( 'grunt-contrib-jasmine' );
180
 
181
+ grunt.registerTask( 'scripts', [ 'browserify', 'jasmine' ] );
182
+ grunt.registerTask( 'styles', [ 'sass' ] );
183
  grunt.registerTask( 'default', [ 'scripts', 'styles' ] );
184
  grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] );
185
+ grunt.registerTask( 'readme', ['wp_readme_to_markdown']);
186
 
187
  grunt.util.linefeed = '\n';
188
 
composer.json CHANGED
@@ -9,5 +9,13 @@
9
  "email": "tech@fusion.net",
10
  "homepage": "http://fusion.net"
11
  }
12
- ]
 
 
 
 
 
 
 
13
  }
 
9
  "email": "tech@fusion.net",
10
  "homepage": "http://fusion.net"
11
  }
12
+ ],
13
+ "require-dev": {
14
+ "wp-coding-standards/wpcs": "dev-develop"
15
+ },
16
+ "scripts": {
17
+ "post-install-cmd": "\"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs",
18
+ "post-update-cmd" : "\"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs"
19
+ }
20
  }
21
+
css/sass/shortcode-ui-editor-styles.scss CHANGED
@@ -1,8 +1,6 @@
1
  .wpview-wrap {
2
- min-height: 48px;
3
 
4
  &.wp-mce-view-show-toolbar {
5
-
6
  .toolbar {
7
  display: block;
8
  }
@@ -13,42 +11,10 @@
13
  font-weight: bold;
14
  }
15
 
16
- }
17
-
18
- .shortcake-preview .loading-placeholder {
19
- .wpview-loading {
20
- width: 60px;
21
- height: 5px;
22
- overflow: hidden;
23
- background-color: transparent;
24
- margin: 10px auto 0;
25
-
26
- ins {
27
- background-color: #333;
28
- margin: 0 0 0 -60px;
29
- width: 60px;
30
- height: 5px;
31
- display: block;
32
- -webkit-animation: preview-loading 1.3s infinite 1s linear;
33
- animation: preview-loading 1.3s infinite 1s linear;
34
- }
35
  }
36
- }
37
 
38
- @-webkit-keyframes preview-loading {
39
- 0% {
40
- margin-left: -60px;
41
- }
42
- 100% {
43
- margin-left: 60px;
44
- }
45
- }
46
-
47
- @keyframes preview-loading {
48
- 0% {
49
- margin-left: -60px;
50
- }
51
- 100% {
52
- margin-left: 60px;
53
- }
54
  }
1
  .wpview-wrap {
 
2
 
3
  &.wp-mce-view-show-toolbar {
 
4
  .toolbar {
5
  display: block;
6
  }
11
  font-weight: bold;
12
  }
13
 
14
+ .shortcake-empty {
15
+ font-family: Consolas, Monaco, monospace;
16
+ color: #666;
17
+ font-size: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
 
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
css/shortcode-ui-editor-styles.css CHANGED
@@ -1,38 +1,11 @@
1
- .wpview-wrap {
2
- min-height: 48px; }
3
- .wpview-wrap.wp-mce-view-show-toolbar .toolbar {
4
- display: block; }
5
- .wpview-wrap .shortcake-error {
6
- color: red;
7
- font-weight: bold; }
8
-
9
- .shortcake-preview .loading-placeholder .wpview-loading {
10
- width: 60px;
11
- height: 5px;
12
- overflow: hidden;
13
- background-color: transparent;
14
- margin: 10px auto 0; }
15
- .shortcake-preview .loading-placeholder .wpview-loading ins {
16
- background-color: #333;
17
- margin: 0 0 0 -60px;
18
- width: 60px;
19
- height: 5px;
20
- display: block;
21
- -webkit-animation: preview-loading 1.3s infinite 1s linear;
22
- animation: preview-loading 1.3s infinite 1s linear; }
23
-
24
- @-webkit-keyframes preview-loading {
25
- 0% {
26
- margin-left: -60px; }
27
-
28
- 100% {
29
- margin-left: 60px; } }
30
-
31
- @keyframes preview-loading {
32
- 0% {
33
- margin-left: -60px; }
34
-
35
- 100% {
36
- margin-left: 60px; } }
37
 
38
  /*# sourceMappingURL=shortcode-ui-editor-styles.css.map */
1
+ .wpview-wrap.wp-mce-view-show-toolbar .toolbar {
2
+ display: block; }
3
+ .wpview-wrap .shortcake-error {
4
+ color: red;
5
+ font-weight: bold; }
6
+ .wpview-wrap .shortcake-empty {
7
+ font-family: Consolas, Monaco, monospace;
8
+ color: #666;
9
+ font-size: 14px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  /*# sourceMappingURL=shortcode-ui-editor-styles.css.map */
css/shortcode-ui-editor-styles.css.map CHANGED
@@ -5,6 +5,6 @@
5
  "../shortcode-ui-editor-styles.scss"
6
  ],
7
  "sourcesContent": [],
8
- "mappings": "AAAA;EACC,AAAY;EAIb,AAAY,AAA0B;IACnC,AAAS;EAIZ,AAAa;IACX,AAAO;IACP,AAAa;;AAMf,AAAmB,AAAqB;EACtC,AAAO;EACP,AAAQ;EACR,AAAU;EACV,AAAkB;EAClB,AAAQ;EAEV,AAAmB,AAAqB,AAAgB;IACrD,AAAkB;IAClB,AAAQ;IACR,AAAO;IACP,AAAQ;IACR,AAAS;IACT,AAAmB;IACnB,AAAW;;mBAKK;EACnB;IACE,AAAa;;EAEf;IACE,AAAa;;WAIJ;EACX;IACE,AAAa;;EAEf;IACE,AAAa",
9
  "names": []
10
  }
5
  "../shortcode-ui-editor-styles.scss"
6
  ],
7
  "sourcesContent": [],
8
+ "mappings": "AAGA,AAAY,AAA0B;EACnC,AAAS;AAIZ,AAAa;EACX,AAAO;EACP,AAAa;AAGf,AAAa;EACX,AAAa;EACb,AAAO;EACP,AAAW",
9
  "names": []
10
  }
inc/class-shortcode-ui.php CHANGED
@@ -7,10 +7,10 @@ class Shortcode_UI {
7
 
8
  private $shortcodes = array();
9
 
10
- private static $instance = null;
11
 
12
  public static function get_instance() {
13
- if ( null == self::$instance ) {
14
  self::$instance = new self;
15
  self::$instance->setup_actions();
16
  }
@@ -27,25 +27,15 @@ class Shortcode_UI {
27
  }
28
 
29
  private function setup_actions() {
30
- $this->add_editor_style();
31
- add_action( 'wp_enqueue_editor', array( $this, 'action_wp_enqueue_editor' ) );
32
- add_action( 'wp_ajax_do_shortcode', array( $this, 'handle_ajax_do_shortcode' ) );
33
  }
34
 
35
  public function register_shortcode_ui( $shortcode_tag, $args = array() ) {
36
 
37
- $defaults = array(
38
- 'label' => '',
39
- 'attrs' => array(),
40
- 'listItemImage' => '', // src or 'dashicons-' - used in insert list.
41
- 'inner_content' => false,
42
- );
43
-
44
- $args = wp_parse_args( $args, $defaults );
45
-
46
-
47
  // inner_content=true is a valid argument, but we want more detail
48
- if ( is_bool( $args['inner_content'] ) && true === $args['inner_content'] ) {
49
  $args['inner_content'] = array(
50
  'label' => esc_html__( 'Inner Content', 'shortcode-ui' ),
51
  'description' => '',
@@ -53,33 +43,8 @@ class Shortcode_UI {
53
  );
54
  }
55
 
56
- //following code is for backward compatibility
57
- //which will be removed in the next version. (to supports 'attr' => 'content' special case)
58
- $num_attrs = count( $args['attrs'] );
59
- for ( $i = 0; $i < $num_attrs; $i++ ) {
60
- if ( ! isset( $args['attrs'][ $i ]['attr'] ) || $args['attrs'][ $i ]['attr'] !== 'content' ) {
61
- continue;
62
- }
63
-
64
- $args['inner_content'] = array();
65
- foreach ( $args['attrs'][ $i ] as $key => $value ) {
66
- if ( $key == 'attr' ) {
67
- continue;
68
- }
69
- $args['inner_content'][ $key ] = $value;
70
- }
71
-
72
- $index = $i;
73
- }
74
- if ( isset( $index ) ) {
75
- array_splice( $args['attrs'], $index, 1 );
76
- }
77
-
78
- // strip invalid
79
- foreach ( $args as $key => $value ) {
80
- if ( ! array_key_exists( $key, $defaults ) ) {
81
- unset( $args[ $key ] );
82
- }
83
  }
84
 
85
  $args['shortcode_tag'] = $shortcode_tag;
@@ -99,7 +64,11 @@ class Shortcode_UI {
99
 
100
  }
101
 
102
- public function add_editor_style() {
 
 
 
 
103
  add_editor_style( $this->plugin_url . '/css/shortcode-ui-editor-styles.css' );
104
  }
105
 
@@ -113,22 +82,37 @@ class Shortcode_UI {
113
  wp_enqueue_media();
114
 
115
  $shortcodes = array_values( $this->shortcodes );
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  usort( $shortcodes, array( $this, 'compare_shortcodes_by_label' ) );
117
 
118
  wp_enqueue_script( 'shortcode-ui', $this->plugin_url . 'js/build/shortcode-ui.js', array( 'jquery', 'backbone', 'mce-view' ), $this->plugin_version );
119
  wp_enqueue_style( 'shortcode-ui', $this->plugin_url . 'css/shortcode-ui.css', array(), $this->plugin_version );
120
  wp_localize_script( 'shortcode-ui', ' shortcodeUIData', array(
121
- 'shortcodes' => $shortcodes,
122
- 'strings' => array(
123
- 'media_frame_title' => esc_html__( 'Insert Post Element', 'shortcode-ui' ),
124
- 'media_frame_menu_insert_label' => esc_html__( 'Insert Post Element', 'shortcode-ui' ),
125
- 'media_frame_menu_update_label' => esc_html__( 'Post Element Details', 'shortcode-ui' ),
126
- 'media_frame_toolbar_insert_label' => esc_html__( 'Insert Element', 'shortcode-ui' ),
127
- 'media_frame_toolbar_update_label' => esc_html__( 'Update', 'shortcode-ui' ),
128
- 'edit_tab_label' => esc_html__( 'Edit', 'shortcode-ui' ),
129
- 'preview_tab_label' => esc_html__( 'Preview', 'shortcode-ui' ),
130
- 'mce_view_error' => esc_html__( 'Failed to load preview', 'shortcode-ui' ),
131
- 'search_placeholder' => esc_html__( 'Search', 'shortcode-ui' ),
 
 
132
  ),
133
  'nonces' => array(
134
  'preview' => wp_create_nonce( 'shortcode-ui-preview' ),
@@ -158,9 +142,9 @@ class Shortcode_UI {
158
  * @return null
159
  */
160
  public function action_admin_print_footer_scripts() {
161
- echo $this->get_view( 'media-frame' );
162
- echo $this->get_view( 'list-item' );
163
- echo $this->get_view( 'edit-form' );
164
 
165
  do_action( 'print_shortcode_ui_templates' );
166
  }
@@ -182,7 +166,6 @@ class Shortcode_UI {
182
  if ( ! file_exists( $template ) ) {
183
  return '';
184
  }
185
-
186
  }
187
 
188
  ob_start();
@@ -211,8 +194,16 @@ class Shortcode_UI {
211
  public function handle_ajax_do_shortcode() {
212
 
213
  // Don't sanitize shortcodes — can contain HTML kses doesn't allow (e.g. sourcecode shortcode)
214
- $shortcode = ! empty( $_POST['shortcode'] ) ? stripslashes( $_POST['shortcode'] ) : null;
215
- $post_id = ! empty( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : null;
 
 
 
 
 
 
 
 
216
 
217
  if ( ! current_user_can( 'edit_post', $post_id ) || ! wp_verify_nonce( $_POST['nonce'], 'shortcode-ui-preview' ) ) {
218
  echo esc_html__( "Something's rotten in the state of Denmark", 'shortcode-ui' );
@@ -220,14 +211,16 @@ class Shortcode_UI {
220
  }
221
 
222
  if ( ! empty( $post_id ) ) {
 
223
  global $post;
224
  $post = get_post( $post_id );
225
  setup_postdata( $post );
 
226
  }
227
 
228
  ob_start();
229
  do_action( 'shortcode_ui_before_do_shortcode', $shortcode );
230
- echo do_shortcode( $shortcode );
231
  do_action( 'shortcode_ui_after_do_shortcode', $shortcode );
232
 
233
  wp_send_json_success( ob_get_clean() );
7
 
8
  private $shortcodes = array();
9
 
10
+ private static $instance;
11
 
12
  public static function get_instance() {
13
+ if ( ! isset( self::$instance ) ) {
14
  self::$instance = new self;
15
  self::$instance->setup_actions();
16
  }
27
  }
28
 
29
  private function setup_actions() {
30
+ add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) );
31
+ add_action( 'wp_enqueue_editor', array( $this, 'action_wp_enqueue_editor' ) );
32
+ add_action( 'wp_ajax_do_shortcode', array( $this, 'handle_ajax_do_shortcode' ) );
33
  }
34
 
35
  public function register_shortcode_ui( $shortcode_tag, $args = array() ) {
36
 
 
 
 
 
 
 
 
 
 
 
37
  // inner_content=true is a valid argument, but we want more detail
38
+ if ( isset( $args['inner_content'] ) && true === $args['inner_content'] ) {
39
  $args['inner_content'] = array(
40
  'label' => esc_html__( 'Inner Content', 'shortcode-ui' ),
41
  'description' => '',
43
  );
44
  }
45
 
46
+ if ( ! isset( $args['attrs'] ) ) {
47
+ $args['attrs'] = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
  $args['shortcode_tag'] = $shortcode_tag;
64
 
65
  }
66
 
67
+ /**
68
+ * Action admin scripts.
69
+ */
70
+ public function action_admin_enqueue_scripts() {
71
+ // Editor styles needs to be added before wp_enqueue_editor
72
  add_editor_style( $this->plugin_url . '/css/shortcode-ui-editor-styles.css' );
73
  }
74
 
82
  wp_enqueue_media();
83
 
84
  $shortcodes = array_values( $this->shortcodes );
85
+ $screen = get_current_screen();
86
+ if ( $screen && ! empty( $screen->post_type ) ) {
87
+ foreach ( $shortcodes as $key => $args ) {
88
+ if ( ! empty( $args['post_type'] ) && ! in_array( $screen->post_type, $args['post_type'] ) ) {
89
+ unset( $shortcodes[ $key ] );
90
+ }
91
+ }
92
+ }
93
+
94
+ if ( empty( $shortcodes ) ) {
95
+ return;
96
+ }
97
+
98
  usort( $shortcodes, array( $this, 'compare_shortcodes_by_label' ) );
99
 
100
  wp_enqueue_script( 'shortcode-ui', $this->plugin_url . 'js/build/shortcode-ui.js', array( 'jquery', 'backbone', 'mce-view' ), $this->plugin_version );
101
  wp_enqueue_style( 'shortcode-ui', $this->plugin_url . 'css/shortcode-ui.css', array(), $this->plugin_version );
102
  wp_localize_script( 'shortcode-ui', ' shortcodeUIData', array(
103
+ 'shortcodes' => $shortcodes,
104
+ 'strings' => array(
105
+ 'media_frame_title' => esc_html__( 'Insert Post Element', 'shortcode-ui' ),
106
+ 'media_frame_menu_insert_label' => esc_html__( 'Insert Post Element', 'shortcode-ui' ),
107
+ 'media_frame_menu_update_label' => esc_html__( '%s Details', 'shortcode-ui' ), // Substituted in JS
108
+ 'media_frame_toolbar_insert_label' => esc_html__( 'Insert Element', 'shortcode-ui' ),
109
+ 'media_frame_toolbar_update_label' => esc_html__( 'Update', 'shortcode-ui' ),
110
+ 'media_frame_no_attributes_message' => esc_html__( 'There are no attributes to configure for this Post Element.', 'shortcode-ui' ),
111
+ 'edit_tab_label' => esc_html__( 'Edit', 'shortcode-ui' ),
112
+ 'preview_tab_label' => esc_html__( 'Preview', 'shortcode-ui' ),
113
+ 'mce_view_error' => esc_html__( 'Failed to load preview', 'shortcode-ui' ),
114
+ 'search_placeholder' => esc_html__( 'Search', 'shortcode-ui' ),
115
+ 'insert_content_label' => esc_html__( 'Insert Content', 'shortcode-ui' ),
116
  ),
117
  'nonces' => array(
118
  'preview' => wp_create_nonce( 'shortcode-ui-preview' ),
142
  * @return null
143
  */
144
  public function action_admin_print_footer_scripts() {
145
+ echo $this->get_view( 'media-frame' ); // WPCS: xss ok
146
+ echo $this->get_view( 'list-item' ); // WPCS: xss ok
147
+ echo $this->get_view( 'edit-form' ); // WPCS: xss ok
148
 
149
  do_action( 'print_shortcode_ui_templates' );
150
  }
166
  if ( ! file_exists( $template ) ) {
167
  return '';
168
  }
 
169
  }
170
 
171
  ob_start();
194
  public function handle_ajax_do_shortcode() {
195
 
196
  // Don't sanitize shortcodes — can contain HTML kses doesn't allow (e.g. sourcecode shortcode)
197
+ if ( ! empty( $_POST['shortcode'] ) ) {
198
+ $shortcode = stripslashes( $_POST['shortcode'] );
199
+ } else {
200
+ $shortcode = null;
201
+ }
202
+ if ( isset( $_POST['post_id'] ) ) {
203
+ $post_id = intval( $_POST['post_id'] );
204
+ } else {
205
+ $post_id = null;
206
+ }
207
 
208
  if ( ! current_user_can( 'edit_post', $post_id ) || ! wp_verify_nonce( $_POST['nonce'], 'shortcode-ui-preview' ) ) {
209
  echo esc_html__( "Something's rotten in the state of Denmark", 'shortcode-ui' );
211
  }
212
 
213
  if ( ! empty( $post_id ) ) {
214
+ // @codingStandardsIgnoreStart
215
  global $post;
216
  $post = get_post( $post_id );
217
  setup_postdata( $post );
218
+ // @codingStandardsIgnoreStart
219
  }
220
 
221
  ob_start();
222
  do_action( 'shortcode_ui_before_do_shortcode', $shortcode );
223
+ echo do_shortcode( $shortcode ); // WPCS: xss ok
224
  do_action( 'shortcode_ui_after_do_shortcode', $shortcode );
225
 
226
  wp_send_json_success( ob_get_clean() );
inc/fields/class-field-attachment.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class Shortcake_Field_Attachment {
4
 
5
- private static $instance = null;
6
 
7
  // All registered post fields.
8
  private $post_fields = array();
@@ -16,7 +16,7 @@ class Shortcake_Field_Attachment {
16
  );
17
 
18
  public static function get_instance() {
19
- if ( null == self::$instance ) {
20
  self::$instance = new self;
21
  self::$instance->setup_actions();
22
  }
@@ -26,7 +26,7 @@ class Shortcake_Field_Attachment {
26
  private function setup_actions() {
27
 
28
  add_filter( 'shortcode_ui_fields', array( $this, 'filter_shortcode_ui_fields' ) );
29
- add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ), 100 );
30
  add_action( 'shortcode_ui_loaded_editor', array( $this, 'action_shortcode_ui_loaded_editor' ) );
31
 
32
  }
@@ -35,13 +35,13 @@ class Shortcake_Field_Attachment {
35
  return array_merge( $fields, $this->fields );
36
  }
37
 
38
- public function action_admin_enqueue_scripts() {
39
 
40
- $script = plugins_url( '/js/build/field-attachment.js', dirname( dirname( __FILE__ ) ) );
41
 
42
  wp_enqueue_script( 'shortcake-field-attachment', $script, array( 'shortcode-ui' ) );
43
 
44
- wp_localize_script( 'shortcake-field-attachment', 'ShorcakeImageFieldData', array(
45
  'defaultArgs' => array(
46
  'libraryType' => null, // array of mime types. eg image, image/jpg, application, application/pdf.
47
  'addButton' => __( 'Select Attachment', 'shortcode-ui' ),
2
 
3
  class Shortcake_Field_Attachment {
4
 
5
+ private static $instance;
6
 
7
  // All registered post fields.
8
  private $post_fields = array();
16
  );
17
 
18
  public static function get_instance() {
19
+ if ( ! isset( self::$instance ) ) {
20
  self::$instance = new self;
21
  self::$instance->setup_actions();
22
  }
26
  private function setup_actions() {
27
 
28
  add_filter( 'shortcode_ui_fields', array( $this, 'filter_shortcode_ui_fields' ) );
29
+ add_action( 'enqueue_shortcode_ui', array( $this, 'action_enqueue_shortcode_ui' ) );
30
  add_action( 'shortcode_ui_loaded_editor', array( $this, 'action_shortcode_ui_loaded_editor' ) );
31
 
32
  }
35
  return array_merge( $fields, $this->fields );
36
  }
37
 
38
+ public function action_enqueue_shortcode_ui() {
39
 
40
+ $script = plugins_url( 'js/build/field-attachment.js', dirname( dirname( __FILE__ ) ) );
41
 
42
  wp_enqueue_script( 'shortcake-field-attachment', $script, array( 'shortcode-ui' ) );
43
 
44
+ wp_localize_script( 'shortcake-field-attachment', 'ShortcakeImageFieldData', array(
45
  'defaultArgs' => array(
46
  'libraryType' => null, // array of mime types. eg image, image/jpg, application, application/pdf.
47
  'addButton' => __( 'Select Attachment', 'shortcode-ui' ),
inc/fields/class-field-color.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class Shortcake_Field_Color {
4
 
5
- private static $instance = null;
6
 
7
  // All registered post fields.
8
  private $post_fields = array();
@@ -16,7 +16,7 @@ class Shortcake_Field_Color {
16
  );
17
 
18
  public static function get_instance() {
19
- if ( null == self::$instance ) {
20
  self::$instance = new self;
21
  self::$instance->setup_actions();
22
  }
@@ -33,7 +33,8 @@ class Shortcake_Field_Color {
33
 
34
  private function color_attribute_present() {
35
 
36
- foreach( Shortcode_UI::get_instance()->get_shortcodes() as $shortcode ) {
 
37
  if ( empty( $shortcode['attrs'] ) ) {
38
  continue;
39
  }
@@ -43,7 +44,7 @@ class Shortcake_Field_Color {
43
  continue;
44
  }
45
 
46
- if ( $attribute['type'] == 'color' ) {
47
  return true;
48
  }
49
  }
@@ -63,7 +64,7 @@ class Shortcake_Field_Color {
63
  return;
64
  }
65
 
66
- $script = plugins_url( '/js/build/field-color.js', dirname( dirname( __FILE__ ) ) );
67
 
68
  wp_enqueue_script( 'shortcake-field-color', $script, array( 'shortcode-ui' ) );
69
 
2
 
3
  class Shortcake_Field_Color {
4
 
5
+ private static $instance;
6
 
7
  // All registered post fields.
8
  private $post_fields = array();
16
  );
17
 
18
  public static function get_instance() {
19
+ if ( ! isset( self::$instance ) ) {
20
  self::$instance = new self;
21
  self::$instance->setup_actions();
22
  }
33
 
34
  private function color_attribute_present() {
35
 
36
+ foreach ( Shortcode_UI::get_instance()->get_shortcodes() as $shortcode ) {
37
+
38
  if ( empty( $shortcode['attrs'] ) ) {
39
  continue;
40
  }
44
  continue;
45
  }
46
 
47
+ if ( 'color' === $attribute['type'] ) {
48
  return true;
49
  }
50
  }
64
  return;
65
  }
66
 
67
+ $script = plugins_url( 'js/build/field-color.js', dirname( dirname( __FILE__ ) ) );
68
 
69
  wp_enqueue_script( 'shortcake-field-color', $script, array( 'shortcode-ui' ) );
70
 
inc/fields/class-field-post-select.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Shortcode_UI_Field_Post_Select {
4
+
5
+ private static $instance;
6
+
7
+ // All registered post fields.
8
+ private $post_fields = array();
9
+
10
+ // Field Settings.
11
+ private $fields = array(
12
+ 'post_select' => array(
13
+ 'template' => 'shortcode-ui-field-post-select',
14
+ 'view' => 'editAttributeFieldPostSelect',
15
+ ),
16
+ );
17
+
18
+ public static function get_instance() {
19
+ if ( ! isset( self::$instance ) ) {
20
+ self::$instance = new self;
21
+ self::$instance->setup_actions();
22
+ }
23
+ return self::$instance;
24
+ }
25
+
26
+ private function setup_actions() {
27
+
28
+ add_filter( 'shortcode_ui_fields', array( $this, 'filter_shortcode_ui_fields' ) );
29
+ add_action( 'enqueue_shortcode_ui', array( $this, 'action_enqueue_shortcode_ui' ) );
30
+ add_action( 'wp_ajax_shortcode_ui_post_field', array( $this, 'action_wp_ajax_shortcode_ui_post_field' ) );
31
+ add_action( 'shortcode_ui_loaded_editor', array( $this, 'action_shortcode_ui_loaded_editor' ) );
32
+
33
+ }
34
+
35
+ public function filter_shortcode_ui_fields( $fields ) {
36
+ return array_merge( $fields, $this->fields );
37
+ }
38
+
39
+ public function action_enqueue_shortcode_ui() {
40
+
41
+ $plugin_dir = dirname( dirname( __FILE__ ) );
42
+
43
+ wp_enqueue_script(
44
+ 'shortcode-ui-field-post-select',
45
+ plugins_url( 'js/build/field-post-select.js', $plugin_dir ),
46
+ array( 'shortcode-ui', 'select2' )
47
+ );
48
+
49
+ wp_enqueue_script( 'select2', plugins_url( 'lib/select2/select2.min.js', $plugin_dir ) , array( 'jquery', 'jquery-ui-sortable' ), '3.5.2' );
50
+ wp_enqueue_style( 'select2', plugins_url( 'lib/select2/select2.css', $plugin_dir ), null, '3.5.2' );
51
+
52
+ wp_localize_script( 'shortcode-ui-field-post-select', 'shortcodeUiPostFieldData', array(
53
+ 'nonce' => wp_create_nonce( 'shortcode_ui_field_post_select' ),
54
+ ) );
55
+
56
+ }
57
+
58
+ /**
59
+ * Output styles and templates used by post select field.
60
+ */
61
+ // public function action_print_media_templates() {
62
+ public function action_shortcode_ui_loaded_editor() {
63
+
64
+ ?>
65
+
66
+ <style>
67
+
68
+ .edit-shortcode-form .select2-container {
69
+ min-width: 300px;
70
+ }
71
+
72
+ .edit-shortcode-form .select2-container a {
73
+ transition: none;
74
+ -webkit-transition: none;
75
+ }
76
+
77
+ .wp-admin .select2-drop {
78
+ z-index: 160001;
79
+ }
80
+
81
+ </style>
82
+
83
+ <script type="text/html" id="tmpl-shortcode-ui-field-post-select">
84
+ <div class="field-block">
85
+ <label for="{{ data.id }}">{{ data.label }}</label>
86
+ <input type="text" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" class="shortcode-ui-post-select" />
87
+ </div>
88
+ </script>
89
+
90
+ <?php
91
+ }
92
+
93
+ /**
94
+ * Ajax handler for select2 post field queries.
95
+ * Output JSON containing post data.
96
+ * Requires that shortcode, attr and nonce are passed.
97
+ * Requires that the field has been correctly registred and can be found in $this->post_fields
98
+ * Supports passing page number and search query string.
99
+ *
100
+ * @return null
101
+ */
102
+ public function action_wp_ajax_shortcode_ui_post_field() {
103
+
104
+ $nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( $_GET['nonce'] ) : null;
105
+ $requested_shortcode = isset( $_GET['shortcode'] ) ? sanitize_text_field( $_GET['shortcode'] ) : null;
106
+ $requested_attr = isset( $_GET['attr'] ) ? sanitize_text_field( $_GET['attr'] ) : null;
107
+ $response = array( 'posts' => array(), 'found_posts' => 0, 'posts_per_page' => 0 );
108
+
109
+ $shortcodes = Shortcode_UI::get_instance()->get_shortcodes();
110
+
111
+ if ( ! wp_verify_nonce( $nonce, 'shortcode_ui_field_post_select' ) ) {
112
+ wp_send_json_error( $response );
113
+ }
114
+
115
+ // Shortcode not found.
116
+ if ( ! isset( $shortcodes[ $requested_shortcode ] ) ) {
117
+ wp_send_json_error( $response );
118
+ die;
119
+ }
120
+
121
+ $shortcode = $shortcodes[ $requested_shortcode ];
122
+
123
+ foreach ( $shortcode['attrs'] as $attr ) {
124
+ if ( $attr['attr'] === $requested_attr && isset( $attr['query'] ) ) {
125
+ $query_args = $attr['query'];
126
+ }
127
+ }
128
+
129
+ // Query not found.
130
+ if ( empty( $query_args ) ) {
131
+ wp_send_json_error( $response );
132
+ die;
133
+ }
134
+
135
+ // Hardcoded query args.
136
+ $query_args['fields'] = 'ids';
137
+ $query_args['perm'] = 'readable';
138
+
139
+ if ( isset( $_GET['page'] ) ) {
140
+ $query_args['paged'] = sanitize_text_field( $_GET['page'] );
141
+ }
142
+
143
+ if ( ! empty( $_GET['s'] ) ) {
144
+ $query_args['s'] = sanitize_text_field( $_GET['s'] );
145
+ }
146
+
147
+ if ( ! empty( $_GET['post__in'] ) ) {
148
+ $post__in = is_array( $_GET['post__in'] ) ? $_GET['post__in'] : explode( ',', $_GET['post__in'] );
149
+ $query_args['post__in'] = array_map( 'intval', $post__in );
150
+ $query_args['orderby'] = 'post__in';
151
+ }
152
+
153
+ $query = new WP_Query( $query_args );
154
+
155
+ foreach ( $query->posts as $post_id ) {
156
+ array_push( $response['posts'], array( 'id' => $post_id, 'text' => html_entity_decode( get_the_title( $post_id ) ) ) );
157
+ }
158
+
159
+ $response['found_posts'] = $query->found_posts;
160
+ $response['posts_per_page'] = $query->query_vars['posts_per_page'];
161
+
162
+ wp_send_json_success( $response );
163
+
164
+ }
165
+
166
+ }
inc/fields/class-shortcode-ui-fields.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  class Shortcode_UI_Fields {
4
 
5
- private static $instance = null;
6
 
7
  // Default Field Settings.
8
  private $field_defaults = array(
@@ -40,7 +40,7 @@ class Shortcode_UI_Fields {
40
  );
41
 
42
  public static function get_instance() {
43
- if ( null == self::$instance ) {
44
  self::$instance = new self;
45
  self::$instance->setup_actions();
46
  }
@@ -72,6 +72,15 @@ class Shortcode_UI_Fields {
72
 
73
  }
74
 
 
 
 
 
 
 
 
 
 
75
  public function action_enqueue_shortcode_ui() {
76
 
77
  wp_localize_script( 'shortcode-ui', 'shortcodeUIFieldData', $this->fields );
2
 
3
  class Shortcode_UI_Fields {
4
 
5
+ private static $instance;
6
 
7
  // Default Field Settings.
8
  private $field_defaults = array(
40
  );
41
 
42
  public static function get_instance() {
43
+ if ( ! isset( self::$instance ) ) {
44
  self::$instance = new self;
45
  self::$instance->setup_actions();
46
  }
72
 
73
  }
74
 
75
+ /**
76
+ * Get all registered fields
77
+ *
78
+ * @return array
79
+ */
80
+ public function get_fields() {
81
+ return $this->fields;
82
+ }
83
+
84
  public function action_enqueue_shortcode_ui() {
85
 
86
  wp_localize_script( 'shortcode-ui', 'shortcodeUIFieldData', $this->fields );
inc/templates/edit-form.tpl.php CHANGED
@@ -17,8 +17,8 @@
17
 
18
  <script type="text/html" id="tmpl-shortcode-ui-field-text">
19
  <div class="field-block">
20
- <label for="{{ data.attr }}">{{ data.label }}</label>
21
- <input type="text" name="{{ data.attr }}" id="{{ data.attr }}" value="{{ data.value }}" placeholder="{{ data.placeholder }}"/>
22
  <# if ( typeof data.description == 'string' ) { #>
23
  <p class="description">{{ data.description }}</p>
24
  <# } #>
@@ -27,8 +27,8 @@
27
 
28
  <script type="text/html" id="tmpl-shortcode-ui-field-url">
29
  <div class="field-block">
30
- <label for="{{ data.attr }}">{{ data.label }}</label>
31
- <input type="url" name="{{ data.attr }}" id="{{ data.attr }}" value="{{ data.value }}" class="code" placeholder="{{ data.placeholder }}"/>
32
  <# if ( typeof data.description == 'string' ) { #>
33
  <p class="description">{{ data.description }}</p>
34
  <# } #>
@@ -37,8 +37,8 @@
37
 
38
  <script type="text/html" id="tmpl-shortcode-ui-field-textarea">
39
  <div class="field-block">
40
- <label for="{{ data.attr }}">{{ data.label }}</label>
41
- <textarea name="{{ data.attr }}" id="{{ data.attr }}" placeholder="{{ data.placeholder }}">{{ data.value }}</textarea>
42
  <# if ( typeof data.description == 'string' ) { #>
43
  <p class="description">{{ data.description }}</p>
44
  <# } #>
@@ -47,8 +47,8 @@
47
 
48
  <script type="text/html" id="tmpl-shortcode-ui-field-select">
49
  <div class="field-block">
50
- <label for="{{ data.attr }}">{{ data.label }}</label>
51
- <select name="{{ data.attr }}" id="{{ data.attr }}">
52
  <# _.each( data.options, function( label, value ) { #>
53
  <option value="{{ value }}" <# if ( value == data.value ){ print('selected'); } #>>{{ label }}</option>
54
  <# }); #>
@@ -61,9 +61,12 @@
61
 
62
  <script type="text/html" id="tmpl-shortcode-ui-field-radio">
63
  <div class="field-block">
64
- <label for="{{ data.attr }}">{{ data.label }}</label>
65
  <# _.each( data.options, function( label, value ) { #>
66
- <input type="radio" name="{{ data.attr }}" value="{{ value }}" <# if ( value == data.value ){ print('checked'); } #>>{{ label }}<br />
 
 
 
67
  <# }); #>
68
  <# if ( typeof data.description == 'string' ) { #>
69
  <p class="description">{{ data.description }}</p>
@@ -73,8 +76,8 @@
73
 
74
  <script type="text/html" id="tmpl-shortcode-ui-field-checkbox">
75
  <div class="field-block">
76
- <label for="{{ data.attr }}">
77
- <input type="checkbox" name="{{ data.attr }}" id="{{ data.attr }}" value="{{ data.value }}" <# if ( 'true' == data.value ){ print('checked'); } #>>
78
  {{ data.label }}
79
  </label>
80
  <# if ( typeof data.description == 'string' ) { #>
@@ -85,8 +88,8 @@
85
 
86
  <script type="text/html" id="tmpl-shortcode-ui-field-email">
87
  <div class="field-block">
88
- <label for="{{ data.attr }}">{{ data.label }}</label>
89
- <input type="email" name="{{ data.attr }}" id="{{ data.attr }}" value="{{ data.value}}" placeholder="{{ data.placeholder }}"/>
90
  <# if ( typeof data.description == 'string' ) { #>
91
  <p class="description">{{ data.description }}</p>
92
  <# } #>
@@ -95,8 +98,8 @@
95
 
96
  <script type="text/html" id="tmpl-shortcode-ui-field-number">
97
  <div class="field-block">
98
- <label for="{{ data.attr }}">{{ data.label }}</label>
99
- <input type="number" name="{{ data.attr }}" id="{{ data.attr }}" value="{{ data.value}}" placeholder="{{ data.placeholder }}"/>
100
  <# if ( typeof data.description == 'string' ) { #>
101
  <p class="description">{{ data.description }}</p>
102
  <# } #>
@@ -105,8 +108,8 @@
105
 
106
  <script type="text/html" id="tmpl-shortcode-ui-field-hidden">
107
  <div class="field-block">
108
- <label for="{{ data.attr }}">{{ data.label }}</label>
109
- <input type="hidden" name="{{ data.attr }}" id="{{ data.attr }}" value="true" />
110
  <# if ( typeof data.description == 'string' ) { #>
111
  <p class="description">{{ data.description }}</p>
112
  <# } #>
@@ -115,8 +118,8 @@
115
 
116
  <script type="text/html" id="tmpl-shortcode-ui-field-date">
117
  <div class="field-block">
118
- <label for="{{ data.attr }}">{{ data.label }}</label>
119
- <input type="date" name="{{ data.attr }}" id="{{ data.attr }}" value="{{ data.value }}" placeholder="{{ data.placeholder }}"/>
120
  <# if ( typeof data.description == 'string' ) { #>
121
  <p class="description">{{ data.description }}</p>
122
  <# } #>
@@ -126,7 +129,7 @@
126
  <script type="text/html" id="tmpl-shortcode-ui-content">
127
  <div class="field-block">
128
  <label for="inner_content">{{ data.label }}</label>
129
- <textarea id="inner_content" name="inner_content" class="content-edit">{{ data.value }}</textarea>
130
  <# if ( typeof data.description == 'string' ) { #>
131
  <p class="description">{{ data.description }}</p>
132
  <# } #>
17
 
18
  <script type="text/html" id="tmpl-shortcode-ui-field-text">
19
  <div class="field-block">
20
+ <label for="{{ data.id }}">{{ data.label }}</label>
21
+ <input type="text" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" {{{ data.meta }}}/>
22
  <# if ( typeof data.description == 'string' ) { #>
23
  <p class="description">{{ data.description }}</p>
24
  <# } #>
27
 
28
  <script type="text/html" id="tmpl-shortcode-ui-field-url">
29
  <div class="field-block">
30
+ <label for="{{ data.id }}">{{ data.label }}</label>
31
+ <input type="url" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" class="code" {{{ data.meta }}}/>
32
  <# if ( typeof data.description == 'string' ) { #>
33
  <p class="description">{{ data.description }}</p>
34
  <# } #>
37
 
38
  <script type="text/html" id="tmpl-shortcode-ui-field-textarea">
39
  <div class="field-block">
40
+ <label for="{{ data.id }}">{{ data.label }}</label>
41
+ <textarea name="{{ data.attr }}" id="{{ data.id }}" {{{ data.meta }}}>{{ data.value }}</textarea>
42
  <# if ( typeof data.description == 'string' ) { #>
43
  <p class="description">{{ data.description }}</p>
44
  <# } #>
47
 
48
  <script type="text/html" id="tmpl-shortcode-ui-field-select">
49
  <div class="field-block">
50
+ <label for="{{ data.id }}">{{ data.label }}</label>
51
+ <select name="{{ data.attr }}" id="{{ data.id }}" {{{ data.meta }}}>
52
  <# _.each( data.options, function( label, value ) { #>
53
  <option value="{{ value }}" <# if ( value == data.value ){ print('selected'); } #>>{{ label }}</option>
54
  <# }); #>
61
 
62
  <script type="text/html" id="tmpl-shortcode-ui-field-radio">
63
  <div class="field-block">
64
+ <label>{{ data.label }}</label>
65
  <# _.each( data.options, function( label, value ) { #>
66
+ <label>
67
+ <input type="radio" name="{{ data.attr }}" value="{{ value }}" <# if ( value == data.value ) { print('checked'); } #> />
68
+ {{ label }}
69
+ </label>
70
  <# }); #>
71
  <# if ( typeof data.description == 'string' ) { #>
72
  <p class="description">{{ data.description }}</p>
76
 
77
  <script type="text/html" id="tmpl-shortcode-ui-field-checkbox">
78
  <div class="field-block">
79
+ <label for="{{ data.id }}">
80
+ <input type="checkbox" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" <# if ( 'true' == data.value ){ print('checked'); } #>>
81
  {{ data.label }}
82
  </label>
83
  <# if ( typeof data.description == 'string' ) { #>
88
 
89
  <script type="text/html" id="tmpl-shortcode-ui-field-email">
90
  <div class="field-block">
91
+ <label for="{{ data.id }}">{{ data.label }}</label>
92
+ <input type="email" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value}}" {{{ data.meta }}}/>
93
  <# if ( typeof data.description == 'string' ) { #>
94
  <p class="description">{{ data.description }}</p>
95
  <# } #>
98
 
99
  <script type="text/html" id="tmpl-shortcode-ui-field-number">
100
  <div class="field-block">
101
+ <label for="{{ data.id }}">{{ data.label }}</label>
102
+ <input type="number" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value}}" {{{ data.meta }}}/>
103
  <# if ( typeof data.description == 'string' ) { #>
104
  <p class="description">{{ data.description }}</p>
105
  <# } #>
108
 
109
  <script type="text/html" id="tmpl-shortcode-ui-field-hidden">
110
  <div class="field-block">
111
+ <label for="{{ data.id }}">{{ data.label }}</label>
112
+ <input type="hidden" name="{{ data.attr }}" id="{{ data.id }}" value="true" {{{ data.meta }}}/>
113
  <# if ( typeof data.description == 'string' ) { #>
114
  <p class="description">{{ data.description }}</p>
115
  <# } #>
118
 
119
  <script type="text/html" id="tmpl-shortcode-ui-field-date">
120
  <div class="field-block">
121
+ <label for="{{ data.id }}">{{ data.label }}</label>
122
+ <input type="date" name="{{ data.attr }}" id="{{ data.id }}" value="{{ data.value }}" {{{ data.meta }}}/>
123
  <# if ( typeof data.description == 'string' ) { #>
124
  <p class="description">{{ data.description }}</p>
125
  <# } #>
129
  <script type="text/html" id="tmpl-shortcode-ui-content">
130
  <div class="field-block">
131
  <label for="inner_content">{{ data.label }}</label>
132
+ <textarea id="inner_content" name="inner_content" class="content-edit" {{{ data.meta }}}>{{ data.value }}</textarea>
133
  <# if ( typeof data.description == 'string' ) { #>
134
  <p class="description">{{ data.description }}</p>
135
  <# } #>
js/build/field-attachment.js CHANGED
@@ -36,7 +36,7 @@ module.exports = Shortcodes;
36
  (function (global){
37
  var sui = require('./utils/sui.js'),
38
  editAttributeField = require('./views/edit-attribute-field.js'),
39
- jQuery = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
40
 
41
  // Cache attachment IDs for quicker loading.
42
  var iDCache = {};
@@ -48,9 +48,9 @@ sui.views.editAttributeFieldAttachment = editAttributeField.extend( {
48
  var model = this.model;
49
 
50
  // Set model default values.
51
- for ( var arg in ShorcakeImageFieldData.defaultArgs ) {
52
  if ( ! model.get( arg ) ) {
53
- model.set( arg, ShorcakeImageFieldData.defaultArgs[ arg ] );
54
  }
55
  }
56
 
@@ -108,23 +108,23 @@ sui.views.editAttributeFieldAttachment = editAttributeField.extend( {
108
  */
109
  var renderPreview = function( attachment ) {
110
 
111
- var $thumbnail = jQuery('<div class="thumbnail"></div>');
112
 
113
  if ( 'image' !== attachment.type ) {
114
 
115
- jQuery( '<img/>', {
116
  src: attachment.icon,
117
  alt: attachment.title,
118
  } ).appendTo( $thumbnail );
119
 
120
- jQuery( '<div/>', {
121
  class: 'filename',
122
  html: '<div>' + attachment.title + '</div>',
123
  } ).appendTo( $thumbnail );
124
 
125
  } else {
126
 
127
- jQuery( '<img/>', {
128
  src: attachment.sizes.thumbnail.url,
129
  width: attachment.sizes.thumbnail.width,
130
  height: attachment.sizes.thumbnail.height,
@@ -192,7 +192,12 @@ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global
192
  * Shortcode Attribute Model.
193
  */
194
  var InnerContent = Backbone.Model.extend({
195
- defaults : false,
 
 
 
 
 
196
  });
197
 
198
  module.exports = InnerContent;
@@ -208,7 +213,10 @@ var ShortcodeAttribute = Backbone.Model.extend({
208
  label: '',
209
  type: '',
210
  value: '',
211
- placeholder: '',
 
 
 
212
  },
213
  });
214
 
@@ -227,7 +235,6 @@ Shortcode = Backbone.Model.extend({
227
  label: '',
228
  shortcode_tag: '',
229
  attrs: new ShortcodeAttributes,
230
- inner_content: new InnerContent,
231
  },
232
 
233
  /**
@@ -240,7 +247,7 @@ Shortcode = Backbone.Model.extend({
240
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
241
  }
242
 
243
- if ( attributes.inner_content !== undefined && ! ( attributes.inner_content instanceof InnerContent ) ) {
244
  attributes.inner_content = new InnerContent( attributes.inner_content );
245
  }
246
 
@@ -253,10 +260,10 @@ Shortcode = Backbone.Model.extend({
253
  */
254
  toJSON: function( options ) {
255
  options = Backbone.Model.prototype.toJSON.call(this, options);
256
- if ( options.attrs !== undefined && ( options.attrs instanceof ShortcodeAttributes ) ) {
257
  options.attrs = options.attrs.toJSON();
258
  }
259
- if ( options.inner_content !== undefined && ( options.inner_content instanceof InnerContent ) ) {
260
  options.inner_content = options.inner_content.toJSON();
261
  }
262
  return options;
@@ -269,7 +276,9 @@ Shortcode = Backbone.Model.extend({
269
  clone: function() {
270
  var clone = Backbone.Model.prototype.clone.call( this );
271
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
272
- clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
 
 
273
  return clone;
274
  },
275
 
@@ -293,7 +302,7 @@ Shortcode = Backbone.Model.extend({
293
 
294
  } );
295
 
296
- if ( 'undefined' !== typeof this.get( 'inner_content' ).get( 'value' ) && this.get( 'inner_content' ).get( 'value').length > 0 ) {
297
  content = this.get( 'inner_content' ).get( 'value' );
298
  }
299
 
@@ -329,8 +338,9 @@ module.exports = window.Shortcode_UI;
329
 
330
  },{"./../collections/shortcodes.js":2}],8:[function(require,module,exports){
331
  (function (global){
332
- var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
333
- sui = require('./../utils/sui.js');
 
334
 
335
  var editAttributeField = Backbone.View.extend( {
336
 
@@ -349,7 +359,42 @@ var editAttributeField = Backbone.View.extend( {
349
  },
350
 
351
  render: function() {
352
- this.$el.html( this.template( this.model.toJSON() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  return this
354
  },
355
 
@@ -362,9 +407,9 @@ var editAttributeField = Backbone.View.extend( {
362
  updateValue: function( e ) {
363
 
364
  if ( this.model.get( 'attr' ) ) {
365
- var $el = jQuery( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
366
  } else {
367
- var $el = jQuery( this.el ).find( '[name="inner_content"]' );
368
  }
369
 
370
  if ( 'radio' === this.model.attributes.type ) {
36
  (function (global){
37
  var sui = require('./utils/sui.js'),
38
  editAttributeField = require('./views/edit-attribute-field.js'),
39
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
40
 
41
  // Cache attachment IDs for quicker loading.
42
  var iDCache = {};
48
  var model = this.model;
49
 
50
  // Set model default values.
51
+ for ( var arg in ShortcakeImageFieldData.defaultArgs ) {
52
  if ( ! model.get( arg ) ) {
53
+ model.set( arg, ShortcakeImageFieldData.defaultArgs[ arg ] );
54
  }
55
  }
56
 
108
  */
109
  var renderPreview = function( attachment ) {
110
 
111
+ var $thumbnail = $('<div class="thumbnail"></div>');
112
 
113
  if ( 'image' !== attachment.type ) {
114
 
115
+ $( '<img/>', {
116
  src: attachment.icon,
117
  alt: attachment.title,
118
  } ).appendTo( $thumbnail );
119
 
120
+ $( '<div/>', {
121
  class: 'filename',
122
  html: '<div>' + attachment.title + '</div>',
123
  } ).appendTo( $thumbnail );
124
 
125
  } else {
126
 
127
+ $( '<img/>', {
128
  src: attachment.sizes.thumbnail.url,
129
  width: attachment.sizes.thumbnail.width,
130
  height: attachment.sizes.thumbnail.height,
192
  * Shortcode Attribute Model.
193
  */
194
  var InnerContent = Backbone.Model.extend({
195
+ defaults : {
196
+ label: shortcodeUIData.strings.insert_content_label,
197
+ type: 'textarea',
198
+ value: '',
199
+ placeholder: '',
200
+ },
201
  });
202
 
203
  module.exports = InnerContent;
213
  label: '',
214
  type: '',
215
  value: '',
216
+ description: '',
217
+ meta: {
218
+ placeholder: '',
219
+ }
220
  },
221
  });
222
 
235
  label: '',
236
  shortcode_tag: '',
237
  attrs: new ShortcodeAttributes,
 
238
  },
239
 
240
  /**
247
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
248
  }
249
 
250
+ if ( attributes.inner_content && ! ( attributes.inner_content instanceof InnerContent ) ) {
251
  attributes.inner_content = new InnerContent( attributes.inner_content );
252
  }
253
 
260
  */
261
  toJSON: function( options ) {
262
  options = Backbone.Model.prototype.toJSON.call(this, options);
263
+ if ( options.attrs && ( options.attrs instanceof ShortcodeAttributes ) ) {
264
  options.attrs = options.attrs.toJSON();
265
  }
266
+ if ( options.inner_content && ( options.inner_content instanceof InnerContent ) ) {
267
  options.inner_content = options.inner_content.toJSON();
268
  }
269
  return options;
276
  clone: function() {
277
  var clone = Backbone.Model.prototype.clone.call( this );
278
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
279
+ if ( clone.get( 'inner_content' ) ) {
280
+ clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
281
+ }
282
  return clone;
283
  },
284
 
302
 
303
  } );
304
 
305
+ if ( this.get( 'inner_content' ) ) {
306
  content = this.get( 'inner_content' ).get( 'value' );
307
  }
308
 
338
 
339
  },{"./../collections/shortcodes.js":2}],8:[function(require,module,exports){
340
  (function (global){
341
+ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
342
+ sui = require('./../utils/sui.js'),
343
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
344
 
345
  var editAttributeField = Backbone.View.extend( {
346
 
359
  },
360
 
361
  render: function() {
362
+
363
+ var data = jQuery.extend( {
364
+ id: 'shortcode-ui-' + this.model.get( 'attr' ) + '-' + this.model.cid,
365
+ }, this.model.toJSON() );
366
+
367
+ // Handle legacy custom meta.
368
+ // Can be removed in 0.4.
369
+ if ( data.placeholder ) {
370
+ data.meta.placeholder = data.placeholder;
371
+ delete data.placeholder;
372
+ }
373
+
374
+ // Convert meta JSON to attribute string.
375
+ var _meta = [];
376
+ for ( var key in data.meta ) {
377
+
378
+ // Boolean attributes can only require attribute key, not value.
379
+ if ( 'boolean' === typeof( data.meta[ key ] ) ) {
380
+
381
+ // Only set truthy boolean attributes.
382
+ if ( data.meta[ key ] ) {
383
+ _meta.push( _.escape( key ) );
384
+ }
385
+
386
+ } else {
387
+
388
+ _meta.push( _.escape( key ) + '="' + _.escape( data.meta[ key ] ) + '"' );
389
+
390
+ }
391
+
392
+ }
393
+
394
+ data.meta = _meta.join( ' ' );
395
+
396
+ this.$el.html( this.template( data ) );
397
+
398
  return this
399
  },
400
 
407
  updateValue: function( e ) {
408
 
409
  if ( this.model.get( 'attr' ) ) {
410
+ var $el = $( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
411
  } else {
412
+ var $el = $( this.el ).find( '[name="inner_content"]' );
413
  }
414
 
415
  if ( 'radio' === this.model.attributes.type ) {
js/build/field-color.js CHANGED
@@ -36,7 +36,7 @@ module.exports = Shortcodes;
36
  (function (global){
37
  var sui = require('./utils/sui.js'),
38
  editAttributeField = require('./views/edit-attribute-field.js'),
39
- jQuery = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
40
 
41
  sui.views.editAttributeFieldColor = editAttributeField.extend( {
42
 
@@ -45,7 +45,7 @@ sui.views.editAttributeFieldColor = editAttributeField.extend( {
45
 
46
  this.$el.find('input[type="text"]:not(.wp-color-picker)').wpColorPicker({
47
  change: function() {
48
- jQuery(this).trigger('keyup');
49
  }
50
  });
51
 
@@ -63,7 +63,12 @@ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global
63
  * Shortcode Attribute Model.
64
  */
65
  var InnerContent = Backbone.Model.extend({
66
- defaults : false,
 
 
 
 
 
67
  });
68
 
69
  module.exports = InnerContent;
@@ -79,7 +84,10 @@ var ShortcodeAttribute = Backbone.Model.extend({
79
  label: '',
80
  type: '',
81
  value: '',
82
- placeholder: '',
 
 
 
83
  },
84
  });
85
 
@@ -98,7 +106,6 @@ Shortcode = Backbone.Model.extend({
98
  label: '',
99
  shortcode_tag: '',
100
  attrs: new ShortcodeAttributes,
101
- inner_content: new InnerContent,
102
  },
103
 
104
  /**
@@ -111,7 +118,7 @@ Shortcode = Backbone.Model.extend({
111
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
112
  }
113
 
114
- if ( attributes.inner_content !== undefined && ! ( attributes.inner_content instanceof InnerContent ) ) {
115
  attributes.inner_content = new InnerContent( attributes.inner_content );
116
  }
117
 
@@ -124,10 +131,10 @@ Shortcode = Backbone.Model.extend({
124
  */
125
  toJSON: function( options ) {
126
  options = Backbone.Model.prototype.toJSON.call(this, options);
127
- if ( options.attrs !== undefined && ( options.attrs instanceof ShortcodeAttributes ) ) {
128
  options.attrs = options.attrs.toJSON();
129
  }
130
- if ( options.inner_content !== undefined && ( options.inner_content instanceof InnerContent ) ) {
131
  options.inner_content = options.inner_content.toJSON();
132
  }
133
  return options;
@@ -140,7 +147,9 @@ Shortcode = Backbone.Model.extend({
140
  clone: function() {
141
  var clone = Backbone.Model.prototype.clone.call( this );
142
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
143
- clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
 
 
144
  return clone;
145
  },
146
 
@@ -164,7 +173,7 @@ Shortcode = Backbone.Model.extend({
164
 
165
  } );
166
 
167
- if ( 'undefined' !== typeof this.get( 'inner_content' ).get( 'value' ) && this.get( 'inner_content' ).get( 'value').length > 0 ) {
168
  content = this.get( 'inner_content' ).get( 'value' );
169
  }
170
 
@@ -200,8 +209,9 @@ module.exports = window.Shortcode_UI;
200
 
201
  },{"./../collections/shortcodes.js":2}],8:[function(require,module,exports){
202
  (function (global){
203
- var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
204
- sui = require('./../utils/sui.js');
 
205
 
206
  var editAttributeField = Backbone.View.extend( {
207
 
@@ -220,7 +230,42 @@ var editAttributeField = Backbone.View.extend( {
220
  },
221
 
222
  render: function() {
223
- this.$el.html( this.template( this.model.toJSON() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  return this
225
  },
226
 
@@ -233,9 +278,9 @@ var editAttributeField = Backbone.View.extend( {
233
  updateValue: function( e ) {
234
 
235
  if ( this.model.get( 'attr' ) ) {
236
- var $el = jQuery( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
237
  } else {
238
- var $el = jQuery( this.el ).find( '[name="inner_content"]' );
239
  }
240
 
241
  if ( 'radio' === this.model.attributes.type ) {
36
  (function (global){
37
  var sui = require('./utils/sui.js'),
38
  editAttributeField = require('./views/edit-attribute-field.js'),
39
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
40
 
41
  sui.views.editAttributeFieldColor = editAttributeField.extend( {
42
 
45
 
46
  this.$el.find('input[type="text"]:not(.wp-color-picker)').wpColorPicker({
47
  change: function() {
48
+ $(this).trigger('keyup');
49
  }
50
  });
51
 
63
  * Shortcode Attribute Model.
64
  */
65
  var InnerContent = Backbone.Model.extend({
66
+ defaults : {
67
+ label: shortcodeUIData.strings.insert_content_label,
68
+ type: 'textarea',
69
+ value: '',
70
+ placeholder: '',
71
+ },
72
  });
73
 
74
  module.exports = InnerContent;
84
  label: '',
85
  type: '',
86
  value: '',
87
+ description: '',
88
+ meta: {
89
+ placeholder: '',
90
+ }
91
  },
92
  });
93
 
106
  label: '',
107
  shortcode_tag: '',
108
  attrs: new ShortcodeAttributes,
 
109
  },
110
 
111
  /**
118
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
119
  }
120
 
121
+ if ( attributes.inner_content && ! ( attributes.inner_content instanceof InnerContent ) ) {
122
  attributes.inner_content = new InnerContent( attributes.inner_content );
123
  }
124
 
131
  */
132
  toJSON: function( options ) {
133
  options = Backbone.Model.prototype.toJSON.call(this, options);
134
+ if ( options.attrs && ( options.attrs instanceof ShortcodeAttributes ) ) {
135
  options.attrs = options.attrs.toJSON();
136
  }
137
+ if ( options.inner_content && ( options.inner_content instanceof InnerContent ) ) {
138
  options.inner_content = options.inner_content.toJSON();
139
  }
140
  return options;
147
  clone: function() {
148
  var clone = Backbone.Model.prototype.clone.call( this );
149
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
150
+ if ( clone.get( 'inner_content' ) ) {
151
+ clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
152
+ }
153
  return clone;
154
  },
155
 
173
 
174
  } );
175
 
176
+ if ( this.get( 'inner_content' ) ) {
177
  content = this.get( 'inner_content' ).get( 'value' );
178
  }
179
 
209
 
210
  },{"./../collections/shortcodes.js":2}],8:[function(require,module,exports){
211
  (function (global){
212
+ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
213
+ sui = require('./../utils/sui.js'),
214
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
215
 
216
  var editAttributeField = Backbone.View.extend( {
217
 
230
  },
231
 
232
  render: function() {
233
+
234
+ var data = jQuery.extend( {
235
+ id: 'shortcode-ui-' + this.model.get( 'attr' ) + '-' + this.model.cid,
236
+ }, this.model.toJSON() );
237
+
238
+ // Handle legacy custom meta.
239
+ // Can be removed in 0.4.
240
+ if ( data.placeholder ) {
241
+ data.meta.placeholder = data.placeholder;
242
+ delete data.placeholder;
243
+ }
244
+
245
+ // Convert meta JSON to attribute string.
246
+ var _meta = [];
247
+ for ( var key in data.meta ) {
248
+
249
+ // Boolean attributes can only require attribute key, not value.
250
+ if ( 'boolean' === typeof( data.meta[ key ] ) ) {
251
+
252
+ // Only set truthy boolean attributes.
253
+ if ( data.meta[ key ] ) {
254
+ _meta.push( _.escape( key ) );
255
+ }
256
+
257
+ } else {
258
+
259
+ _meta.push( _.escape( key ) + '="' + _.escape( data.meta[ key ] ) + '"' );
260
+
261
+ }
262
+
263
+ }
264
+
265
+ data.meta = _meta.join( ' ' );
266
+
267
+ this.$el.html( this.template( data ) );
268
+
269
  return this
270
  },
271
 
278
  updateValue: function( e ) {
279
 
280
  if ( this.model.get( 'attr' ) ) {
281
+ var $el = $( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
282
  } else {
283
+ var $el = $( this.el ).find( '[name="inner_content"]' );
284
  }
285
 
286
  if ( 'radio' === this.model.attributes.type ) {
js/build/field-post-select.js ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ ( function( $ ) {
3
+
4
+ var sui = window.Shortcode_UI;
5
+
6
+ // Cached Data.
7
+ var postSelectCache = {};
8
+
9
+ sui.views.editAttributeFieldPostSelect = sui.views.editAttributeField.extend( {
10
+
11
+ events: {
12
+ 'change .shortcode-ui-post-select': 'updateValue',
13
+ },
14
+
15
+ render: function() {
16
+
17
+ var self = this,
18
+ defaults = { multiple: false };
19
+
20
+ for ( var arg in defaults ) {
21
+ if ( ! this.model.get( arg ) ) {
22
+ this.model.set( arg, defaults[ arg ] );
23
+ }
24
+ }
25
+
26
+ var data = this.model.toJSON();
27
+ data.id = 'shortcode-ui-' + this.model.get( 'attr' ) + '-' + this.model.cid;
28
+
29
+ this.$el.html( this.template( data ) );
30
+
31
+ var ajaxData = {
32
+ action : 'shortcode_ui_post_field',
33
+ nonce : shortcodeUiPostFieldData.nonce,
34
+ shortcode : this.shortcode.get( 'shortcode_tag'),
35
+ attr : this.model.get( 'attr' )
36
+ };
37
+
38
+ var $field = this.$el.find( '.shortcode-ui-post-select' );
39
+
40
+ $field.select2({
41
+
42
+ placeholder: "Search",
43
+ multiple: this.model.get( 'multiple' ),
44
+ ajax: {
45
+ url: ajaxurl,
46
+ dataType: 'json',
47
+ quietMillis: 250,
48
+ data: function (term, page) {
49
+ ajaxData.s = term;
50
+ ajaxData.page = page;
51
+ return ajaxData;
52
+ },
53
+ results: function ( response, page ) {
54
+
55
+ if ( ! response.success ) {
56
+ return { results: {}, more: false };
57
+ }
58
+
59
+ // Cache data for quicker rendering later.
60
+ postSelectCache = $.extend( postSelectCache, response.data.posts );
61
+
62
+ var more = ( page * response.data.posts_per_page ) < response.data.found_posts; // whether or not there are more results available
63
+ return { results: response.data.posts, more: more };
64
+
65
+ },
66
+ },
67
+
68
+ /**
69
+ * Initialize Callback
70
+ * Used to set render the initial value.
71
+ * Has to make a request to get the title for the current ID.
72
+ */
73
+ initSelection: function(element, callback) {
74
+
75
+ var ids, parsedData = [], cached;
76
+
77
+ // Convert stored value to array of IDs (int).
78
+ ids = $(element)
79
+ .val()
80
+ .split(',')
81
+ .map( function (str) { return str.trim(); } )
82
+ .map( function (str) { return parseInt( str ); } )
83
+
84
+ if ( ids.length < 1 ) {
85
+ return;
86
+ }
87
+
88
+ // Check if there is already cached data.
89
+ for ( var i = 0; i < ids.length; i++ ) {
90
+ if ( cached = _.find( postSelectCache, _.matches( { id: ids[i] } ) ) ) {
91
+ parsedData.push( cached );
92
+ }
93
+ };
94
+
95
+ // If not multiple - return single value if we have one.
96
+ if ( parsedData.length && ! self.model.get( 'multiple' ) ) {
97
+ callback( parsedData[0] );
98
+ return;
99
+ }
100
+
101
+ var uncachedIds = _.difference( ids, _.pluck( parsedData, 'id' ) );
102
+
103
+ if ( ! uncachedIds.length ) {
104
+
105
+ callback( parsedData );
106
+
107
+ } else {
108
+
109
+ var initAjaxData = jQuery.extend( true, {}, ajaxData );
110
+ initAjaxData.action = 'shortcode_ui_post_field';
111
+ initAjaxData.post__in = uncachedIds;
112
+
113
+ $.get( ajaxurl, initAjaxData ).done( function( response ) {
114
+
115
+ if ( ! response.success ) {
116
+ return { results: {}, more: false };
117
+ }
118
+
119
+ postSelectCache = $.extend( postSelectCache, response.data.posts );
120
+
121
+ // If not multi-select, expects single object, not array of objects.
122
+ if ( ! self.model.get( 'multiple' ) ) {
123
+ callback( response.data.posts[0] );
124
+ return;
125
+ }
126
+
127
+ // Append new data to cached data.
128
+ // Sort by original order.
129
+ parsedData = parsedData
130
+ .concat( response.data.posts )
131
+ .sort(function (a, b) {
132
+ if ( ids.indexOf( a.id ) > ids.indexOf( b.id ) ) return 1;
133
+ if ( ids.indexOf( a.id ) < ids.indexOf( b.id ) ) return -1;
134
+ return 0;
135
+ });
136
+
137
+ callback( parsedData );
138
+ return;
139
+
140
+ } );
141
+
142
+ }
143
+
144
+ },
145
+
146
+ } );
147
+
148
+ // Make multiple values sortable.
149
+ if ( this.model.get( 'multiple' ) ) {
150
+ $field.select2('container').find('ul.select2-choices').sortable({
151
+ containment: 'parent',
152
+ start: function() { $('.shortcode-ui-post-select').select2('onSortStart'); },
153
+ update: function() { $('.shortcode-ui-post-select').select2('onSortEnd'); }
154
+ });
155
+ }
156
+
157
+ return this;
158
+
159
+ }
160
+
161
+ } );
162
+
163
+ /**
164
+ * Extending SUI Media Controller to hide Select2 UI Drop-Down when menu
165
+ * changes in Meida modal
166
+ * 1. going back/forth between different shortcakes (refresh)
167
+ * 2. changing the menu in left column (deactivate)
168
+ * 3. @TODO closing the modal.
169
+ */
170
+ var mediaController = sui.controllers.MediaController;
171
+ sui.controllers.MediaController = mediaController.extend({
172
+
173
+ refresh: function(){
174
+ mediaController.prototype.refresh.apply( this, arguments );
175
+ this.destroySelect2UI();
176
+ },
177
+
178
+ //doesn't need to call parent as it already an "abstract" method in parent to provide callback
179
+ deactivate: function() {
180
+ this.destroySelect2UI();
181
+ },
182
+
183
+ destroySelect2UI: function() {
184
+ $('.shortcode-ui-post-select.select2-container').select2( "close" );
185
+ }
186
+
187
+ });
188
+
189
+ /**
190
+ * Extending the SUI Tabbed View to hide Select2 UI dropdown when previewing the shortcake
191
+ */
192
+ var tabbedView = sui.views.TabbedView;
193
+ sui.views.TabbedView = tabbedView.extend({
194
+ tabSwitcher: function() {
195
+ tabbedView.prototype.tabSwitcher.apply( this, arguments );
196
+ $('.shortcode-ui-post-select.select2-container').select2( "close" );
197
+ }
198
+ });
199
+
200
+ } )( jQuery );
201
+
202
+ },{}]},{},[1]);
js/build/shortcode-ui.js CHANGED
@@ -35,7 +35,8 @@ module.exports = Shortcodes;
35
  },{"./../models/shortcode.js":6}],3:[function(require,module,exports){
36
  (function (global){
37
  var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
38
- wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null);
 
39
  Shortcodes = require('./../collections/shortcodes.js');
40
 
41
  var MediaController = wp.media.controller.State.extend({
@@ -87,7 +88,7 @@ sui.controllers.MediaController = MediaController;
87
  module.exports = MediaController;
88
 
89
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
90
- },{"./../collections/shortcodes.js":2}],4:[function(require,module,exports){
91
  (function (global){
92
  var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
93
 
@@ -95,7 +96,12 @@ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global
95
  * Shortcode Attribute Model.
96
  */
97
  var InnerContent = Backbone.Model.extend({
98
- defaults : false,
 
 
 
 
 
99
  });
100
 
101
  module.exports = InnerContent;
@@ -111,7 +117,10 @@ var ShortcodeAttribute = Backbone.Model.extend({
111
  label: '',
112
  type: '',
113
  value: '',
114
- placeholder: '',
 
 
 
115
  },
116
  });
117
 
@@ -130,7 +139,6 @@ Shortcode = Backbone.Model.extend({
130
  label: '',
131
  shortcode_tag: '',
132
  attrs: new ShortcodeAttributes,
133
- inner_content: new InnerContent,
134
  },
135
 
136
  /**
@@ -143,7 +151,7 @@ Shortcode = Backbone.Model.extend({
143
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
144
  }
145
 
146
- if ( attributes.inner_content !== undefined && ! ( attributes.inner_content instanceof InnerContent ) ) {
147
  attributes.inner_content = new InnerContent( attributes.inner_content );
148
  }
149
 
@@ -156,10 +164,10 @@ Shortcode = Backbone.Model.extend({
156
  */
157
  toJSON: function( options ) {
158
  options = Backbone.Model.prototype.toJSON.call(this, options);
159
- if ( options.attrs !== undefined && ( options.attrs instanceof ShortcodeAttributes ) ) {
160
  options.attrs = options.attrs.toJSON();
161
  }
162
- if ( options.inner_content !== undefined && ( options.inner_content instanceof InnerContent ) ) {
163
  options.inner_content = options.inner_content.toJSON();
164
  }
165
  return options;
@@ -172,7 +180,9 @@ Shortcode = Backbone.Model.extend({
172
  clone: function() {
173
  var clone = Backbone.Model.prototype.clone.call( this );
174
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
175
- clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
 
 
176
  return clone;
177
  },
178
 
@@ -196,7 +206,7 @@ Shortcode = Backbone.Model.extend({
196
 
197
  } );
198
 
199
- if ( 'undefined' !== typeof this.get( 'inner_content' ).get( 'value' ) && this.get( 'inner_content' ).get( 'value').length > 0 ) {
200
  content = this.get( 'inner_content' ).get( 'value' );
201
  }
202
 
@@ -226,9 +236,9 @@ var sui = require('./utils/sui.js'),
226
  shortcodeViewConstructor = require('./utils/shortcode-view-constructor.js'),
227
  mediaFrame = require('./views/media-frame.js'),
228
  wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
229
- jQuery = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
230
 
231
- jQuery(document).ready(function(){
232
 
233
  // Create collection of shortcode models from data.
234
  sui.shortcodes.add( shortcodeUIData.shortcodes );
@@ -242,7 +252,7 @@ jQuery(document).ready(function(){
242
  shortcode.get('shortcode_tag'),
243
  // Must extend for 4.1.
244
  // This is handled by wp.mce.views.register in 4.2.
245
- jQuery.extend( true, {}, shortcodeViewConstructor )
246
  );
247
  }
248
  } );
@@ -252,9 +262,9 @@ jQuery(document).ready(function(){
252
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
253
  },{"./collections/shortcodes.js":2,"./utils/shortcode-view-constructor.js":8,"./utils/sui.js":9,"./views/media-frame.js":14}],8:[function(require,module,exports){
254
  (function (global){
255
- sui = require('./sui.js');
256
- wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null);
257
- jQuery = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
258
 
259
  /**
260
  * Generic shortcode mce view constructor.
@@ -264,6 +274,7 @@ var shortcodeViewConstructor = {
264
 
265
  initialize: function( options ) {
266
  this.shortcodeModel = this.getShortcodeModel( this.shortcode );
 
267
  },
268
 
269
  /**
@@ -293,28 +304,17 @@ var shortcodeViewConstructor = {
293
  }
294
  );
295
 
296
- if ('content' in options) {
297
- var inner_content = shortcodeModel.get('inner_content');
298
- inner_content.set('value', options.content)
 
 
299
  }
300
 
301
  return shortcodeModel;
302
 
303
  },
304
 
305
- /**
306
- * Return the preview HTML.
307
- * If empty, fetches data.
308
- *
309
- * @return string
310
- */
311
- getContent : function() {
312
- if ( ! this.content ) {
313
- this.fetch();
314
- }
315
- return this.content;
316
- },
317
-
318
  /**
319
  * Fetch preview.
320
  * Async. Sets this.content and calls this.render.
@@ -330,16 +330,22 @@ var shortcodeViewConstructor = {
330
  this.fetching = true;
331
 
332
  wp.ajax.post( 'do_shortcode', {
333
- post_id: jQuery( '#post_ID' ).val(),
334
  shortcode: this.shortcodeModel.formatShortcode(),
335
  nonce: shortcodeUIData.nonces.preview,
336
  }).done( function( response ) {
337
- self.content = response;
 
 
 
 
 
 
338
  }).fail( function() {
339
  self.content = '<span class="shortcake-error">' + shortcodeUIData.strings.mce_view_error + '</span>';
340
  } ).always( function() {
341
  delete self.fetching;
342
- self.render();
343
  } );
344
 
345
  }
@@ -357,7 +363,7 @@ var shortcodeViewConstructor = {
357
 
358
  // Backwards compatability for WP pre-4.2
359
  if ( 'object' === typeof( shortcodeString ) ) {
360
- shortcodeString = decodeURIComponent( jQuery(shortcodeString).attr('data-wpview-text') );
361
  }
362
 
363
  currentShortcode = this.parseShortcodeString( shortcodeString );
@@ -409,22 +415,33 @@ var shortcodeViewConstructor = {
409
 
410
  if ( matches[2] ) {
411
 
412
- attributeMatches = matches[2].match( /(\S+?=".*?")/g ) || [];
 
 
 
 
 
 
413
 
414
  // convert attribute strings to object.
415
  for ( var i = 0; i < attributeMatches.length; i++ ) {
416
 
417
- var bitsRegEx = /(\S+?)="(.*?)"/g;
418
  var bits = bitsRegEx.exec( attributeMatches[i] );
419
 
420
- attr = currentShortcode.get( 'attrs' ).findWhere({
421
- attr : bits[1]
422
- });
423
 
424
- if ( attr ) {
425
- attr.set('value', bits[2]);
426
- }
 
 
 
 
 
 
427
 
 
428
  }
429
 
430
  }
@@ -445,6 +462,7 @@ var shortcodeViewConstructor = {
445
 
446
  initialize: function( options ) {
447
  this.shortcode = this.getShortcode( options );
 
448
  },
449
 
450
  getShortcode: function( options ) {
@@ -472,7 +490,9 @@ var shortcodeViewConstructor = {
472
 
473
  if ('content' in options.shortcode) {
474
  var inner_content = shortcode.get('inner_content');
475
- inner_content.set('value', options.shortcode.content)
 
 
476
  }
477
 
478
  return shortcode;
@@ -486,7 +506,7 @@ var shortcodeViewConstructor = {
486
  if ( ! this.parsed ) {
487
 
488
  wp.ajax.post( 'do_shortcode', {
489
- post_id: jQuery( '#post_ID' ).val(),
490
  shortcode: this.shortcode.formatShortcode(),
491
  nonce: shortcodeUIData.nonces.preview,
492
  }).done( function( response ) {
@@ -514,11 +534,6 @@ var shortcodeViewConstructor = {
514
  * @return string html
515
  */
516
  getHtml : function() {
517
-
518
- if ( ! this.parsed ) {
519
- this.fetch();
520
- }
521
-
522
  return this.parsed;
523
  },
524
 
@@ -571,8 +586,9 @@ module.exports = window.Shortcode_UI;
571
 
572
  },{"./../collections/shortcodes.js":2}],10:[function(require,module,exports){
573
  (function (global){
574
- var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
575
- sui = require('./../utils/sui.js');
 
576
 
577
  var editAttributeField = Backbone.View.extend( {
578
 
@@ -591,7 +607,42 @@ var editAttributeField = Backbone.View.extend( {
591
  },
592
 
593
  render: function() {
594
- this.$el.html( this.template( this.model.toJSON() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
595
  return this
596
  },
597
 
@@ -604,9 +655,9 @@ var editAttributeField = Backbone.View.extend( {
604
  updateValue: function( e ) {
605
 
606
  if ( this.model.get( 'attr' ) ) {
607
- var $el = jQuery( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
608
  } else {
609
- var $el = jQuery( this.el ).find( '[name="inner_content"]' );
610
  }
611
 
612
  if ( 'radio' === this.model.attributes.type ) {
@@ -628,6 +679,7 @@ module.exports = editAttributeField;
628
  (function (global){
629
  var wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
630
  sui = require('./../utils/sui.js'),
 
631
  editAttributeField = require('./edit-attribute-field.js');
632
 
633
  /**
@@ -641,7 +693,7 @@ var EditShortcodeForm = wp.Backbone.View.extend({
641
  var t = this;
642
 
643
  var innerContent = this.model.get( 'inner_content' );
644
- if ( typeof innerContent.attributes.type !== 'undefined' ) {
645
 
646
  // add UI for inner_content
647
  var view = new editAttributeField( {
@@ -663,17 +715,37 @@ var EditShortcodeForm = wp.Backbone.View.extend({
663
  return;
664
  }
665
 
 
 
 
 
 
 
 
666
  var viewObjName = shortcodeUIFieldData[ type ].view;
667
  var tmplName = shortcodeUIFieldData[ type ].template;
668
 
669
- var view = new sui.views[viewObjName]( { model: attr } );
670
- view.template = wp.media.template( tmplName );
671
  view.shortcode = t.model;
672
 
673
  t.views.add( '.edit-shortcode-form-fields', view );
674
 
675
  } );
676
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  },
678
 
679
  });
@@ -764,6 +836,7 @@ module.exports = insertShortcodeList;
764
  },{"./../collections/shortcodes.js":2,"./insert-shortcode-list-item.js":12}],14:[function(require,module,exports){
765
  (function (global){
766
  var wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
 
767
  MediaController = require('./../controllers/media-controller.js'),
768
  Shortcode_UI = require('./shortcode-ui'),
769
  Toolbar = require('./media-toolbar');
@@ -790,17 +863,17 @@ var mediaFrame = postMediaFrame.extend( {
790
  };
791
 
792
  if ( 'currentShortcode' in this.options ) {
793
- opts.title = shortcodeUIData.strings.media_frame_menu_update_label;
794
  }
795
 
796
- var controller = new MediaController( opts );
797
 
798
  if ( 'currentShortcode' in this.options ) {
799
- controller.props.set( 'currentShortcode', arguments[0].currentShortcode );
800
- controller.props.set( 'action', 'update' );
801
  }
802
 
803
- this.states.add([ controller]);
804
 
805
  this.on( 'content:render:' + id + '-content-insert', _.bind( this.contentRender, this, 'shortcode-ui', 'insert' ) );
806
  this.on( 'toolbar:create:' + id + '-toolbar', this.toolbarCreate, this );
@@ -809,6 +882,19 @@ var mediaFrame = postMediaFrame.extend( {
809
 
810
  },
811
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  contentRender : function( id, tab ) {
813
  this.content.set(
814
  new Shortcode_UI( {
@@ -955,7 +1041,8 @@ module.exports = SearchShortcode;
955
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
956
  },{"./../utils/sui.js":9}],17:[function(require,module,exports){
957
  (function (global){
958
- var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
 
959
 
960
  /**
961
  * Preview of rendered shortcode.
@@ -1018,8 +1105,10 @@ var ShortcodePreview = Backbone.View.extend({
1018
 
1019
  _.defaults( params || {}, { 'head': '', 'body': '', 'body_classes': 'shortcake shortcake-preview' });
1020
 
1021
- $iframe = jQuery( '<iframe/>', {
1022
- src: tinymce.Env.ie ? 'javascript:""' : '',
 
 
1023
  frameBorder: '0',
1024
  allowTransparency: 'true',
1025
  scrolling: 'no',
@@ -1033,10 +1122,10 @@ var ShortcodePreview = Backbone.View.extend({
1033
  */
1034
  $iframe.load( function() {
1035
 
1036
- self.autoresizeIframe( jQuery(this) );
1037
 
1038
- var head = jQuery(this).contents().find('head'),
1039
- body = jQuery(this).contents().find('body');
1040
 
1041
  head.html( params.head );
1042
  body.html( params.body );
@@ -1052,7 +1141,7 @@ var ShortcodePreview = Backbone.View.extend({
1052
  * Watch for mutations in iFrame content.
1053
  * resize iFrame height on change.
1054
  *
1055
- * @param jQuery object $iframe
1056
  */
1057
  autoresizeIframe: function( $iframe ) {
1058
 
@@ -1099,7 +1188,7 @@ var ShortcodePreview = Backbone.View.extend({
1099
  fetchShortcode: function( callback ) {
1100
 
1101
  wp.ajax.post( 'do_shortcode', {
1102
- post_id: jQuery( '#post_ID' ).val(),
1103
  shortcode: this.model.formatShortcode(),
1104
  nonce: shortcodeUIData.nonces.preview,
1105
  }).done( function( response ) {
@@ -1119,7 +1208,8 @@ var ShortcodePreview = Backbone.View.extend({
1119
  getEditorStyles: function() {
1120
  var styles = {};
1121
 
1122
- _.each( tinymce.editors, function( editor ) {
 
1123
  _.each( editor.dom.$( 'link[rel="stylesheet"]', editor.getDoc().head ), function( link ) {
1124
  var href;
1125
  ( href = link.href ) && ( styles[href] = true ); // Poor man's de-duping.
@@ -1127,7 +1217,7 @@ var ShortcodePreview = Backbone.View.extend({
1127
  });
1128
 
1129
  styles = _.map( _.keys( styles ), function( href ) {
1130
- return jQuery( '<link rel="stylesheet" type="text/css">' ).attr( 'href', href )[0].outerHTML;
1131
  });
1132
 
1133
  return styles;
@@ -1146,8 +1236,8 @@ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global
1146
  EditShortcodeForm = require('./edit-shortcode-form.js'),
1147
  Toolbar = require('./media-toolbar.js'),
1148
  SearchShortcode = require('./search-shortcode.js'),
1149
- sui = require('./../utils/sui.js');
1150
- jQuery = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
1151
 
1152
  var Shortcode_UI = Backbone.View.extend({
1153
 
@@ -1247,7 +1337,7 @@ var Shortcode_UI = Backbone.View.extend({
1247
 
1248
  select: function(e) {
1249
  this.controller.props.set( 'action', 'insert' );
1250
- var target = jQuery(e.currentTarget).closest( '.shortcode-list-item' );
1251
  var shortcode = sui.shortcodes.findWhere( { shortcode_tag: target.attr( 'data-shortcode' ) } );
1252
 
1253
  if ( ! shortcode ) {
@@ -1269,6 +1359,7 @@ module.exports = Shortcode_UI;
1269
  (function (global){
1270
  var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
1271
  var sui = require('./../utils/sui.js');
 
1272
 
1273
  /**
1274
  * Abstraction to manage tabbed content. Tab parameters (e.g., label) along with
@@ -1341,7 +1432,7 @@ var TabbedView = Backbone.View.extend({
1341
  event.stopPropagation();
1342
  event.preventDefault();
1343
 
1344
- var target = jQuery(event.currentTarget).attr('data-target');
1345
 
1346
  this.select(target);
1347
  },
35
  },{"./../models/shortcode.js":6}],3:[function(require,module,exports){
36
  (function (global){
37
  var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
38
+ wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
39
+ sui = require('./../utils/sui.js'),
40
  Shortcodes = require('./../collections/shortcodes.js');
41
 
42
  var MediaController = wp.media.controller.State.extend({
88
  module.exports = MediaController;
89
 
90
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
91
+ },{"./../collections/shortcodes.js":2,"./../utils/sui.js":9}],4:[function(require,module,exports){
92
  (function (global){
93
  var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
94
 
96
  * Shortcode Attribute Model.
97
  */
98
  var InnerContent = Backbone.Model.extend({
99
+ defaults : {
100
+ label: shortcodeUIData.strings.insert_content_label,
101
+ type: 'textarea',
102
+ value: '',
103
+ placeholder: '',
104
+ },
105
  });
106
 
107
  module.exports = InnerContent;
117
  label: '',
118
  type: '',
119
  value: '',
120
+ description: '',
121
+ meta: {
122
+ placeholder: '',
123
+ }
124
  },
125
  });
126
 
139
  label: '',
140
  shortcode_tag: '',
141
  attrs: new ShortcodeAttributes,
 
142
  },
143
 
144
  /**
151
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
152
  }
153
 
154
+ if ( attributes.inner_content && ! ( attributes.inner_content instanceof InnerContent ) ) {
155
  attributes.inner_content = new InnerContent( attributes.inner_content );
156
  }
157
 
164
  */
165
  toJSON: function( options ) {
166
  options = Backbone.Model.prototype.toJSON.call(this, options);
167
+ if ( options.attrs && ( options.attrs instanceof ShortcodeAttributes ) ) {
168
  options.attrs = options.attrs.toJSON();
169
  }
170
+ if ( options.inner_content && ( options.inner_content instanceof InnerContent ) ) {
171
  options.inner_content = options.inner_content.toJSON();
172
  }
173
  return options;
180
  clone: function() {
181
  var clone = Backbone.Model.prototype.clone.call( this );
182
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
183
+ if ( clone.get( 'inner_content' ) ) {
184
+ clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
185
+ }
186
  return clone;
187
  },
188
 
206
 
207
  } );
208
 
209
+ if ( this.get( 'inner_content' ) ) {
210
  content = this.get( 'inner_content' ).get( 'value' );
211
  }
212
 
236
  shortcodeViewConstructor = require('./utils/shortcode-view-constructor.js'),
237
  mediaFrame = require('./views/media-frame.js'),
238
  wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
239
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
240
 
241
+ $(document).ready(function(){
242
 
243
  // Create collection of shortcode models from data.
244
  sui.shortcodes.add( shortcodeUIData.shortcodes );
252
  shortcode.get('shortcode_tag'),
253
  // Must extend for 4.1.
254
  // This is handled by wp.mce.views.register in 4.2.
255
+ $.extend( true, {}, shortcodeViewConstructor )
256
  );
257
  }
258
  } );
262
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
263
  },{"./collections/shortcodes.js":2,"./utils/shortcode-view-constructor.js":8,"./utils/sui.js":9,"./views/media-frame.js":14}],8:[function(require,module,exports){
264
  (function (global){
265
+ var sui = require('./sui.js'),
266
+ wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
267
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
268
 
269
  /**
270
  * Generic shortcode mce view constructor.
274
 
275
  initialize: function( options ) {
276
  this.shortcodeModel = this.getShortcodeModel( this.shortcode );
277
+ this.fetch();
278
  },
279
 
280
  /**
304
  }
305
  );
306
 
307
+ if ( 'content' in options ) {
308
+ var innerContent = shortcodeModel.get('inner_content');
309
+ if ( innerContent ) {
310
+ innerContent.set('value', options.content)
311
+ }
312
  }
313
 
314
  return shortcodeModel;
315
 
316
  },
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  /**
319
  * Fetch preview.
320
  * Async. Sets this.content and calls this.render.
330
  this.fetching = true;
331
 
332
  wp.ajax.post( 'do_shortcode', {
333
+ post_id: $( '#post_ID' ).val(),
334
  shortcode: this.shortcodeModel.formatShortcode(),
335
  nonce: shortcodeUIData.nonces.preview,
336
  }).done( function( response ) {
337
+
338
+ if ( '' === response ) {
339
+ self.content = '<span class="shortcake-notice shortcake-empty">' + self.shortcodeModel.formatShortcode() + '</span>';
340
+ } else {
341
+ self.content = response;
342
+ }
343
+
344
  }).fail( function() {
345
  self.content = '<span class="shortcake-error">' + shortcodeUIData.strings.mce_view_error + '</span>';
346
  } ).always( function() {
347
  delete self.fetching;
348
+ self.render( null, true );
349
  } );
350
 
351
  }
363
 
364
  // Backwards compatability for WP pre-4.2
365
  if ( 'object' === typeof( shortcodeString ) ) {
366
+ shortcodeString = decodeURIComponent( $(shortcodeString).attr('data-wpview-text') );
367
  }
368
 
369
  currentShortcode = this.parseShortcodeString( shortcodeString );
415
 
416
  if ( matches[2] ) {
417
 
418
+ var attributeRegex = /(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/gmi;
419
+ attributeMatches = matches[2].match( attributeRegex ) || [];
420
+
421
+ // Trim whitespace from matches.
422
+ attributeMatches = attributeMatches.map( function( match ) {
423
+ return match.replace( /^\s+|\s+$/g, '' );
424
+ } );
425
 
426
  // convert attribute strings to object.
427
  for ( var i = 0; i < attributeMatches.length; i++ ) {
428
 
429
+ var bitsRegEx = /(\S+?)=(.*)/g;
430
  var bits = bitsRegEx.exec( attributeMatches[i] );
431
 
432
+ if ( bits && bits[1] ) {
 
 
433
 
434
+ attr = currentShortcode.get( 'attrs' ).findWhere({
435
+ attr : bits[1]
436
+ });
437
+
438
+ // If attribute found - set value.
439
+ // Trim quotes from beginning and end.
440
+ if ( attr ) {
441
+ attr.set( 'value', bits[2].replace( /^"|^'|"$|'$/gmi, "" ) );
442
+ }
443
 
444
+ }
445
  }
446
 
447
  }
462
 
463
  initialize: function( options ) {
464
  this.shortcode = this.getShortcode( options );
465
+ this.fetch();
466
  },
467
 
468
  getShortcode: function( options ) {
490
 
491
  if ('content' in options.shortcode) {
492
  var inner_content = shortcode.get('inner_content');
493
+ if ( inner_content ) {
494
+ inner_content.set('value', options.shortcode.content)
495
+ }
496
  }
497
 
498
  return shortcode;
506
  if ( ! this.parsed ) {
507
 
508
  wp.ajax.post( 'do_shortcode', {
509
+ post_id: $( '#post_ID' ).val(),
510
  shortcode: this.shortcode.formatShortcode(),
511
  nonce: shortcodeUIData.nonces.preview,
512
  }).done( function( response ) {
534
  * @return string html
535
  */
536
  getHtml : function() {
 
 
 
 
 
537
  return this.parsed;
538
  },
539
 
586
 
587
  },{"./../collections/shortcodes.js":2}],10:[function(require,module,exports){
588
  (function (global){
589
+ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
590
+ sui = require('./../utils/sui.js'),
591
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
592
 
593
  var editAttributeField = Backbone.View.extend( {
594
 
607
  },
608
 
609
  render: function() {
610
+
611
+ var data = jQuery.extend( {
612
+ id: 'shortcode-ui-' + this.model.get( 'attr' ) + '-' + this.model.cid,
613
+ }, this.model.toJSON() );
614
+
615
+ // Handle legacy custom meta.
616
+ // Can be removed in 0.4.
617
+ if ( data.placeholder ) {
618
+ data.meta.placeholder = data.placeholder;
619
+ delete data.placeholder;
620
+ }
621
+
622
+ // Convert meta JSON to attribute string.
623
+ var _meta = [];
624
+ for ( var key in data.meta ) {
625
+
626
+ // Boolean attributes can only require attribute key, not value.
627
+ if ( 'boolean' === typeof( data.meta[ key ] ) ) {
628
+
629
+ // Only set truthy boolean attributes.
630
+ if ( data.meta[ key ] ) {
631
+ _meta.push( _.escape( key ) );
632
+ }
633
+
634
+ } else {
635
+
636
+ _meta.push( _.escape( key ) + '="' + _.escape( data.meta[ key ] ) + '"' );
637
+
638
+ }
639
+
640
+ }
641
+
642
+ data.meta = _meta.join( ' ' );
643
+
644
+ this.$el.html( this.template( data ) );
645
+
646
  return this
647
  },
648
 
655
  updateValue: function( e ) {
656
 
657
  if ( this.model.get( 'attr' ) ) {
658
+ var $el = $( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
659
  } else {
660
+ var $el = $( this.el ).find( '[name="inner_content"]' );
661
  }
662
 
663
  if ( 'radio' === this.model.attributes.type ) {
679
  (function (global){
680
  var wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
681
  sui = require('./../utils/sui.js'),
682
+ backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
683
  editAttributeField = require('./edit-attribute-field.js');
684
 
685
  /**
693
  var t = this;
694
 
695
  var innerContent = this.model.get( 'inner_content' );
696
+ if ( innerContent && typeof innerContent.attributes.type !== 'undefined' ) {
697
 
698
  // add UI for inner_content
699
  var view = new editAttributeField( {
715
  return;
716
  }
717
 
718
+ var templateData = {
719
+ value: attr.get('value'),
720
+ attr_raw: {
721
+ name: attr.get('value')
722
+ }
723
+ }
724
+
725
  var viewObjName = shortcodeUIFieldData[ type ].view;
726
  var tmplName = shortcodeUIFieldData[ type ].template;
727
 
728
+ var view = new sui.views[viewObjName]( { model: attr } );
729
+ view.template = wp.media.template( tmplName );
730
  view.shortcode = t.model;
731
 
732
  t.views.add( '.edit-shortcode-form-fields', view );
733
 
734
  } );
735
 
736
+ if ( 0 == this.model.get( 'attrs' ).length && ( ! innerContent || typeof innerContent == 'undefined' ) ) {
737
+ var messageView = new Backbone.View({
738
+ tagName: 'div',
739
+ className: 'notice updated',
740
+ });
741
+ messageView.render = function() {
742
+ this.$el.append( '<p>' );
743
+ this.$el.find('p').text( shortcodeUIData.strings.media_frame_no_attributes_message );
744
+ return this;
745
+ };
746
+ t.views.add( '.edit-shortcode-form-fields', messageView );
747
+ }
748
+
749
  },
750
 
751
  });
836
  },{"./../collections/shortcodes.js":2,"./insert-shortcode-list-item.js":12}],14:[function(require,module,exports){
837
  (function (global){
838
  var wp = (typeof window !== "undefined" ? window.wp : typeof global !== "undefined" ? global.wp : null),
839
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null),
840
  MediaController = require('./../controllers/media-controller.js'),
841
  Shortcode_UI = require('./shortcode-ui'),
842
  Toolbar = require('./media-toolbar');
863
  };
864
 
865
  if ( 'currentShortcode' in this.options ) {
866
+ opts.title = shortcodeUIData.strings.media_frame_menu_update_label.replace( /%s/, this.options.currentShortcode.attributes.label );
867
  }
868
 
869
+ this.mediaController = new MediaController( opts );
870
 
871
  if ( 'currentShortcode' in this.options ) {
872
+ this.mediaController.props.set( 'currentShortcode', arguments[0].currentShortcode );
873
+ this.mediaController.props.set( 'action', 'update' );
874
  }
875
 
876
+ this.states.add([ this.mediaController ]);
877
 
878
  this.on( 'content:render:' + id + '-content-insert', _.bind( this.contentRender, this, 'shortcode-ui', 'insert' ) );
879
  this.on( 'toolbar:create:' + id + '-toolbar', this.toolbarCreate, this );
882
 
883
  },
884
 
885
+ events: function() {
886
+ return _.extend( {}, postMediaFrame.prototype.events, {
887
+ 'click .media-menu-item' : 'resetMediaController',
888
+ } );
889
+ },
890
+
891
+ resetMediaController: function( event ) {
892
+ if ( this.state().props.get('currentShortcode') ) {
893
+ this.mediaController.reset();
894
+ this.contentRender( 'shortcode-ui', 'insert' );
895
+ }
896
+ },
897
+
898
  contentRender : function( id, tab ) {
899
  this.content.set(
900
  new Shortcode_UI( {
1041
  }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
1042
  },{"./../utils/sui.js":9}],17:[function(require,module,exports){
1043
  (function (global){
1044
+ var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null),
1045
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
1046
 
1047
  /**
1048
  * Preview of rendered shortcode.
1105
 
1106
  _.defaults( params || {}, { 'head': '', 'body': '', 'body_classes': 'shortcake shortcake-preview' });
1107
 
1108
+ var isIE = typeof tinymce != 'undefined' ? tinymce.Env.ie : false;
1109
+
1110
+ $iframe = $( '<iframe/>', {
1111
+ src: isIE ? 'javascript:""' : '',
1112
  frameBorder: '0',
1113
  allowTransparency: 'true',
1114
  scrolling: 'no',
1122
  */
1123
  $iframe.load( function() {
1124
 
1125
+ self.autoresizeIframe( $(this) );
1126
 
1127
+ var head = $(this).contents().find('head'),
1128
+ body = $(this).contents().find('body');
1129
 
1130
  head.html( params.head );
1131
  body.html( params.body );
1141
  * Watch for mutations in iFrame content.
1142
  * resize iFrame height on change.
1143
  *
1144
+ * @param $ object $iframe
1145
  */
1146
  autoresizeIframe: function( $iframe ) {
1147
 
1188
  fetchShortcode: function( callback ) {
1189
 
1190
  wp.ajax.post( 'do_shortcode', {
1191
+ post_id: $( '#post_ID' ).val(),
1192
  shortcode: this.model.formatShortcode(),
1193
  nonce: shortcodeUIData.nonces.preview,
1194
  }).done( function( response ) {
1208
  getEditorStyles: function() {
1209
  var styles = {};
1210
 
1211
+ var editors = typeof tinymce != 'undefined' ? tinymce.editors : [];
1212
+ _.each( editors, function( editor ) {
1213
  _.each( editor.dom.$( 'link[rel="stylesheet"]', editor.getDoc().head ), function( link ) {
1214
  var href;
1215
  ( href = link.href ) && ( styles[href] = true ); // Poor man's de-duping.
1217
  });
1218
 
1219
  styles = _.map( _.keys( styles ), function( href ) {
1220
+ return $( '<link rel="stylesheet" type="text/css">' ).attr( 'href', href )[0].outerHTML;
1221
  });
1222
 
1223
  return styles;
1236
  EditShortcodeForm = require('./edit-shortcode-form.js'),
1237
  Toolbar = require('./media-toolbar.js'),
1238
  SearchShortcode = require('./search-shortcode.js'),
1239
+ sui = require('./../utils/sui.js'),
1240
+ $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
1241
 
1242
  var Shortcode_UI = Backbone.View.extend({
1243
 
1337
 
1338
  select: function(e) {
1339
  this.controller.props.set( 'action', 'insert' );
1340
+ var target = $(e.currentTarget).closest( '.shortcode-list-item' );
1341
  var shortcode = sui.shortcodes.findWhere( { shortcode_tag: target.attr( 'data-shortcode' ) } );
1342
 
1343
  if ( ! shortcode ) {
1359
  (function (global){
1360
  var Backbone = (typeof window !== "undefined" ? window.Backbone : typeof global !== "undefined" ? global.Backbone : null);
1361
  var sui = require('./../utils/sui.js');
1362
+ var $ = (typeof window !== "undefined" ? window.jQuery : typeof global !== "undefined" ? global.jQuery : null);
1363
 
1364
  /**
1365
  * Abstraction to manage tabbed content. Tab parameters (e.g., label) along with
1432
  event.stopPropagation();
1433
  event.preventDefault();
1434
 
1435
+ var target = $(event.currentTarget).attr('data-target');
1436
 
1437
  this.select(target);
1438
  },
js/{collections → src/collections}/shortcode-attributes.js RENAMED
File without changes
js/{collections → src/collections}/shortcodes.js RENAMED
File without changes
js/{controllers → src/controllers}/media-controller.js RENAMED
@@ -1,5 +1,6 @@
1
  var Backbone = require('backbone'),
2
- wp = require('wp');
 
3
  Shortcodes = require('sui-collections/shortcodes');
4
 
5
  var MediaController = wp.media.controller.State.extend({
1
  var Backbone = require('backbone'),
2
+ wp = require('wp'),
3
+ sui = require('sui-utils/sui'),
4
  Shortcodes = require('sui-collections/shortcodes');
5
 
6
  var MediaController = wp.media.controller.State.extend({
js/{field-attachment.js → src/field-attachment.js} RENAMED
@@ -1,6 +1,6 @@
1
  var sui = require('sui-utils/sui'),
2
  editAttributeField = require( 'sui-views/edit-attribute-field' ),
3
- jQuery = require('jquery');
4
 
5
  // Cache attachment IDs for quicker loading.
6
  var iDCache = {};
@@ -12,9 +12,9 @@ sui.views.editAttributeFieldAttachment = editAttributeField.extend( {
12
  var model = this.model;
13
 
14
  // Set model default values.
15
- for ( var arg in ShorcakeImageFieldData.defaultArgs ) {
16
  if ( ! model.get( arg ) ) {
17
- model.set( arg, ShorcakeImageFieldData.defaultArgs[ arg ] );
18
  }
19
  }
20
 
@@ -72,23 +72,23 @@ sui.views.editAttributeFieldAttachment = editAttributeField.extend( {
72
  */
73
  var renderPreview = function( attachment ) {
74
 
75
- var $thumbnail = jQuery('<div class="thumbnail"></div>');
76
 
77
  if ( 'image' !== attachment.type ) {
78
 
79
- jQuery( '<img/>', {
80
  src: attachment.icon,
81
  alt: attachment.title,
82
  } ).appendTo( $thumbnail );
83
 
84
- jQuery( '<div/>', {
85
  class: 'filename',
86
  html: '<div>' + attachment.title + '</div>',
87
  } ).appendTo( $thumbnail );
88
 
89
  } else {
90
 
91
- jQuery( '<img/>', {
92
  src: attachment.sizes.thumbnail.url,
93
  width: attachment.sizes.thumbnail.width,
94
  height: attachment.sizes.thumbnail.height,
1
  var sui = require('sui-utils/sui'),
2
  editAttributeField = require( 'sui-views/edit-attribute-field' ),
3
+ $ = require('jquery');
4
 
5
  // Cache attachment IDs for quicker loading.
6
  var iDCache = {};
12
  var model = this.model;
13
 
14
  // Set model default values.
15
+ for ( var arg in ShortcakeImageFieldData.defaultArgs ) {
16
  if ( ! model.get( arg ) ) {
17
+ model.set( arg, ShortcakeImageFieldData.defaultArgs[ arg ] );
18
  }
19
  }
20
 
72
  */
73
  var renderPreview = function( attachment ) {
74
 
75
+ var $thumbnail = $('<div class="thumbnail"></div>');
76
 
77
  if ( 'image' !== attachment.type ) {
78
 
79
+ $( '<img/>', {
80
  src: attachment.icon,
81
  alt: attachment.title,
82
  } ).appendTo( $thumbnail );
83
 
84
+ $( '<div/>', {
85
  class: 'filename',
86
  html: '<div>' + attachment.title + '</div>',
87
  } ).appendTo( $thumbnail );
88
 
89
  } else {
90
 
91
+ $( '<img/>', {
92
  src: attachment.sizes.thumbnail.url,
93
  width: attachment.sizes.thumbnail.width,
94
  height: attachment.sizes.thumbnail.height,
js/{field-color.js → src/field-color.js} RENAMED
@@ -1,6 +1,6 @@
1
  var sui = require('sui-utils/sui'),
2
  editAttributeField = require( 'sui-views/edit-attribute-field' ),
3
- jQuery = require('jquery');
4
 
5
  sui.views.editAttributeFieldColor = editAttributeField.extend( {
6
 
@@ -9,7 +9,7 @@ sui.views.editAttributeFieldColor = editAttributeField.extend( {
9
 
10
  this.$el.find('input[type="text"]:not(.wp-color-picker)').wpColorPicker({
11
  change: function() {
12
- jQuery(this).trigger('keyup');
13
  }
14
  });
15
 
1
  var sui = require('sui-utils/sui'),
2
  editAttributeField = require( 'sui-views/edit-attribute-field' ),
3
+ $ = require('jquery');
4
 
5
  sui.views.editAttributeFieldColor = editAttributeField.extend( {
6
 
9
 
10
  this.$el.find('input[type="text"]:not(.wp-color-picker)').wpColorPicker({
11
  change: function() {
12
+ $(this).trigger('keyup');
13
  }
14
  });
15
 
js/src/field-post-select.js ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+
3
+ var sui = window.Shortcode_UI;
4
+
5
+ // Cached Data.
6
+ var postSelectCache = {};
7
+
8
+ sui.views.editAttributeFieldPostSelect = sui.views.editAttributeField.extend( {
9
+
10
+ events: {
11
+ 'change .shortcode-ui-post-select': 'updateValue',
12
+ },
13
+
14
+ render: function() {
15
+
16
+ var self = this,
17
+ defaults = { multiple: false };
18
+
19
+ for ( var arg in defaults ) {
20
+ if ( ! this.model.get( arg ) ) {
21
+ this.model.set( arg, defaults[ arg ] );
22
+ }
23
+ }
24
+
25
+ var data = this.model.toJSON();
26
+ data.id = 'shortcode-ui-' + this.model.get( 'attr' ) + '-' + this.model.cid;
27
+
28
+ this.$el.html( this.template( data ) );
29
+
30
+ var ajaxData = {
31
+ action : 'shortcode_ui_post_field',
32
+ nonce : shortcodeUiPostFieldData.nonce,
33
+ shortcode : this.shortcode.get( 'shortcode_tag'),
34
+ attr : this.model.get( 'attr' )
35
+ };
36
+
37
+ var $field = this.$el.find( '.shortcode-ui-post-select' );
38
+
39
+ $field.select2({
40
+
41
+ placeholder: "Search",
42
+ multiple: this.model.get( 'multiple' ),
43
+ ajax: {
44
+ url: ajaxurl,
45
+ dataType: 'json',
46
+ quietMillis: 250,
47
+ data: function (term, page) {
48
+ ajaxData.s = term;
49
+ ajaxData.page = page;
50
+ return ajaxData;
51
+ },
52
+ results: function ( response, page ) {
53
+
54
+ if ( ! response.success ) {
55
+ return { results: {}, more: false };
56
+ }
57
+
58
+ // Cache data for quicker rendering later.
59
+ postSelectCache = $.extend( postSelectCache, response.data.posts );
60
+
61
+ var more = ( page * response.data.posts_per_page ) < response.data.found_posts; // whether or not there are more results available
62
+ return { results: response.data.posts, more: more };
63
+
64
+ },
65
+ },
66
+
67
+ /**
68
+ * Initialize Callback
69
+ * Used to set render the initial value.
70
+ * Has to make a request to get the title for the current ID.
71
+ */
72
+ initSelection: function(element, callback) {
73
+
74
+ var ids, parsedData = [], cached;
75
+
76
+ // Convert stored value to array of IDs (int).
77
+ ids = $(element)
78
+ .val()
79
+ .split(',')
80
+ .map( function (str) { return str.trim(); } )
81
+ .map( function (str) { return parseInt( str ); } )
82
+
83
+ if ( ids.length < 1 ) {
84
+ return;
85
+ }
86
+
87
+ // Check if there is already cached data.
88
+ for ( var i = 0; i < ids.length; i++ ) {
89
+ if ( cached = _.find( postSelectCache, _.matches( { id: ids[i] } ) ) ) {
90
+ parsedData.push( cached );
91
+ }
92
+ };
93
+
94
+ // If not multiple - return single value if we have one.
95
+ if ( parsedData.length && ! self.model.get( 'multiple' ) ) {
96
+ callback( parsedData[0] );
97
+ return;
98
+ }
99
+
100
+ var uncachedIds = _.difference( ids, _.pluck( parsedData, 'id' ) );
101
+
102
+ if ( ! uncachedIds.length ) {
103
+
104
+ callback( parsedData );
105
+
106
+ } else {
107
+
108
+ var initAjaxData = jQuery.extend( true, {}, ajaxData );
109
+ initAjaxData.action = 'shortcode_ui_post_field';
110
+ initAjaxData.post__in = uncachedIds;
111
+
112
+ $.get( ajaxurl, initAjaxData ).done( function( response ) {
113
+
114
+ if ( ! response.success ) {
115
+ return { results: {}, more: false };
116
+ }
117
+
118
+ postSelectCache = $.extend( postSelectCache, response.data.posts );
119
+
120
+ // If not multi-select, expects single object, not array of objects.
121
+ if ( ! self.model.get( 'multiple' ) ) {
122
+ callback( response.data.posts[0] );
123
+ return;
124
+ }
125
+
126
+ // Append new data to cached data.
127
+ // Sort by original order.
128
+ parsedData = parsedData
129
+ .concat( response.data.posts )
130
+ .sort(function (a, b) {
131
+ if ( ids.indexOf( a.id ) > ids.indexOf( b.id ) ) return 1;
132
+ if ( ids.indexOf( a.id ) < ids.indexOf( b.id ) ) return -1;
133
+ return 0;
134
+ });
135
+
136
+ callback( parsedData );
137
+ return;
138
+
139
+ } );
140
+
141
+ }
142
+
143
+ },
144
+
145
+ } );
146
+
147
+ // Make multiple values sortable.
148
+ if ( this.model.get( 'multiple' ) ) {
149
+ $field.select2('container').find('ul.select2-choices').sortable({
150
+ containment: 'parent',
151
+ start: function() { $('.shortcode-ui-post-select').select2('onSortStart'); },
152
+ update: function() { $('.shortcode-ui-post-select').select2('onSortEnd'); }
153
+ });
154
+ }
155
+
156
+ return this;
157
+
158
+ }
159
+
160
+ } );
161
+
162
+ /**
163
+ * Extending SUI Media Controller to hide Select2 UI Drop-Down when menu
164
+ * changes in Meida modal
165
+ * 1. going back/forth between different shortcakes (refresh)
166
+ * 2. changing the menu in left column (deactivate)
167
+ * 3. @TODO closing the modal.
168
+ */
169
+ var mediaController = sui.controllers.MediaController;
170
+ sui.controllers.MediaController = mediaController.extend({
171
+
172
+ refresh: function(){
173
+ mediaController.prototype.refresh.apply( this, arguments );
174
+ this.destroySelect2UI();
175
+ },
176
+
177
+ //doesn't need to call parent as it already an "abstract" method in parent to provide callback
178
+ deactivate: function() {
179
+ this.destroySelect2UI();
180
+ },
181
+
182
+ destroySelect2UI: function() {
183
+ $('.shortcode-ui-post-select.select2-container').select2( "close" );
184
+ }
185
+
186
+ });
187
+
188
+ /**
189
+ * Extending the SUI Tabbed View to hide Select2 UI dropdown when previewing the shortcake
190
+ */
191
+ var tabbedView = sui.views.TabbedView;
192
+ sui.views.TabbedView = tabbedView.extend({
193
+ tabSwitcher: function() {
194
+ tabbedView.prototype.tabSwitcher.apply( this, arguments );
195
+ $('.shortcode-ui-post-select.select2-container').select2( "close" );
196
+ }
197
+ });
198
+
199
+ } )( jQuery );
js/{models → src/models}/inner-content.js RENAMED
@@ -4,7 +4,12 @@ var Backbone = require('backbone');
4
  * Shortcode Attribute Model.
5
  */
6
  var InnerContent = Backbone.Model.extend({
7
- defaults : false,
 
 
 
 
 
8
  });
9
 
10
  module.exports = InnerContent;
4
  * Shortcode Attribute Model.
5
  */
6
  var InnerContent = Backbone.Model.extend({
7
+ defaults : {
8
+ label: shortcodeUIData.strings.insert_content_label,
9
+ type: 'textarea',
10
+ value: '',
11
+ placeholder: '',
12
+ },
13
  });
14
 
15
  module.exports = InnerContent;
js/{models → src/models}/shortcode-attribute.js RENAMED
@@ -6,7 +6,10 @@ var ShortcodeAttribute = Backbone.Model.extend({
6
  label: '',
7
  type: '',
8
  value: '',
9
- placeholder: '',
 
 
 
10
  },
11
  });
12
 
6
  label: '',
7
  type: '',
8
  value: '',
9
+ description: '',
10
+ meta: {
11
+ placeholder: '',
12
+ }
13
  },
14
  });
15
 
js/{models → src/models}/shortcode.js RENAMED
@@ -8,7 +8,6 @@ Shortcode = Backbone.Model.extend({
8
  label: '',
9
  shortcode_tag: '',
10
  attrs: new ShortcodeAttributes,
11
- inner_content: new InnerContent,
12
  },
13
 
14
  /**
@@ -21,7 +20,7 @@ Shortcode = Backbone.Model.extend({
21
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
22
  }
23
 
24
- if ( attributes.inner_content !== undefined && ! ( attributes.inner_content instanceof InnerContent ) ) {
25
  attributes.inner_content = new InnerContent( attributes.inner_content );
26
  }
27
 
@@ -34,10 +33,10 @@ Shortcode = Backbone.Model.extend({
34
  */
35
  toJSON: function( options ) {
36
  options = Backbone.Model.prototype.toJSON.call(this, options);
37
- if ( options.attrs !== undefined && ( options.attrs instanceof ShortcodeAttributes ) ) {
38
  options.attrs = options.attrs.toJSON();
39
  }
40
- if ( options.inner_content !== undefined && ( options.inner_content instanceof InnerContent ) ) {
41
  options.inner_content = options.inner_content.toJSON();
42
  }
43
  return options;
@@ -50,7 +49,9 @@ Shortcode = Backbone.Model.extend({
50
  clone: function() {
51
  var clone = Backbone.Model.prototype.clone.call( this );
52
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
53
- clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
 
 
54
  return clone;
55
  },
56
 
@@ -74,7 +75,7 @@ Shortcode = Backbone.Model.extend({
74
 
75
  } );
76
 
77
- if ( 'undefined' !== typeof this.get( 'inner_content' ).get( 'value' ) && this.get( 'inner_content' ).get( 'value').length > 0 ) {
78
  content = this.get( 'inner_content' ).get( 'value' );
79
  }
80
 
8
  label: '',
9
  shortcode_tag: '',
10
  attrs: new ShortcodeAttributes,
 
11
  },
12
 
13
  /**
20
  attributes.attrs = new ShortcodeAttributes( attributes.attrs );
21
  }
22
 
23
+ if ( attributes.inner_content && ! ( attributes.inner_content instanceof InnerContent ) ) {
24
  attributes.inner_content = new InnerContent( attributes.inner_content );
25
  }
26
 
33
  */
34
  toJSON: function( options ) {
35
  options = Backbone.Model.prototype.toJSON.call(this, options);
36
+ if ( options.attrs && ( options.attrs instanceof ShortcodeAttributes ) ) {
37
  options.attrs = options.attrs.toJSON();
38
  }
39
+ if ( options.inner_content && ( options.inner_content instanceof InnerContent ) ) {
40
  options.inner_content = options.inner_content.toJSON();
41
  }
42
  return options;
49
  clone: function() {
50
  var clone = Backbone.Model.prototype.clone.call( this );
51
  clone.set( 'attrs', clone.get( 'attrs' ).clone() );
52
+ if ( clone.get( 'inner_content' ) ) {
53
+ clone.set( 'inner_content', clone.get( 'inner_content' ).clone() );
54
+ }
55
  return clone;
56
  },
57
 
75
 
76
  } );
77
 
78
+ if ( this.get( 'inner_content' ) ) {
79
  content = this.get( 'inner_content' ).get( 'value' );
80
  }
81
 
js/{shortcode-ui.js → src/shortcode-ui.js} RENAMED
@@ -3,9 +3,9 @@ var sui = require('sui-utils/sui'),
3
  shortcodeViewConstructor = require('sui-utils/shortcode-view-constructor'),
4
  mediaFrame = require('sui-views/media-frame'),
5
  wp = require('wp'),
6
- jQuery = require('jquery');
7
 
8
- jQuery(document).ready(function(){
9
 
10
  // Create collection of shortcode models from data.
11
  sui.shortcodes.add( shortcodeUIData.shortcodes );
@@ -19,7 +19,7 @@ jQuery(document).ready(function(){
19
  shortcode.get('shortcode_tag'),
20
  // Must extend for 4.1.
21
  // This is handled by wp.mce.views.register in 4.2.
22
- jQuery.extend( true, {}, shortcodeViewConstructor )
23
  );
24
  }
25
  } );
3
  shortcodeViewConstructor = require('sui-utils/shortcode-view-constructor'),
4
  mediaFrame = require('sui-views/media-frame'),
5
  wp = require('wp'),
6
+ $ = require('jquery');
7
 
8
+ $(document).ready(function(){
9
 
10
  // Create collection of shortcode models from data.
11
  sui.shortcodes.add( shortcodeUIData.shortcodes );
19
  shortcode.get('shortcode_tag'),
20
  // Must extend for 4.1.
21
  // This is handled by wp.mce.views.register in 4.2.
22
+ $.extend( true, {}, shortcodeViewConstructor )
23
  );
24
  }
25
  } );
js/{utils → src/utils}/shortcode-view-constructor.js RENAMED
@@ -1,6 +1,6 @@
1
- sui = require('sui-utils/sui');
2
- wp = require('wp');
3
- jQuery = require('jquery');
4
 
5
  /**
6
  * Generic shortcode mce view constructor.
@@ -10,6 +10,7 @@ var shortcodeViewConstructor = {
10
 
11
  initialize: function( options ) {
12
  this.shortcodeModel = this.getShortcodeModel( this.shortcode );
 
13
  },
14
 
15
  /**
@@ -39,28 +40,17 @@ var shortcodeViewConstructor = {
39
  }
40
  );
41
 
42
- if ('content' in options) {
43
- var inner_content = shortcodeModel.get('inner_content');
44
- inner_content.set('value', options.content)
 
 
45
  }
46
 
47
  return shortcodeModel;
48
 
49
  },
50
 
51
- /**
52
- * Return the preview HTML.
53
- * If empty, fetches data.
54
- *
55
- * @return string
56
- */
57
- getContent : function() {
58
- if ( ! this.content ) {
59
- this.fetch();
60
- }
61
- return this.content;
62
- },
63
-
64
  /**
65
  * Fetch preview.
66
  * Async. Sets this.content and calls this.render.
@@ -76,16 +66,22 @@ var shortcodeViewConstructor = {
76
  this.fetching = true;
77
 
78
  wp.ajax.post( 'do_shortcode', {
79
- post_id: jQuery( '#post_ID' ).val(),
80
  shortcode: this.shortcodeModel.formatShortcode(),
81
  nonce: shortcodeUIData.nonces.preview,
82
  }).done( function( response ) {
83
- self.content = response;
 
 
 
 
 
 
84
  }).fail( function() {
85
  self.content = '<span class="shortcake-error">' + shortcodeUIData.strings.mce_view_error + '</span>';
86
  } ).always( function() {
87
  delete self.fetching;
88
- self.render();
89
  } );
90
 
91
  }
@@ -103,7 +99,7 @@ var shortcodeViewConstructor = {
103
 
104
  // Backwards compatability for WP pre-4.2
105
  if ( 'object' === typeof( shortcodeString ) ) {
106
- shortcodeString = decodeURIComponent( jQuery(shortcodeString).attr('data-wpview-text') );
107
  }
108
 
109
  currentShortcode = this.parseShortcodeString( shortcodeString );
@@ -155,22 +151,33 @@ var shortcodeViewConstructor = {
155
 
156
  if ( matches[2] ) {
157
 
158
- attributeMatches = matches[2].match( /(\S+?=".*?")/g ) || [];
 
 
 
 
 
 
159
 
160
  // convert attribute strings to object.
161
  for ( var i = 0; i < attributeMatches.length; i++ ) {
162
 
163
- var bitsRegEx = /(\S+?)="(.*?)"/g;
164
  var bits = bitsRegEx.exec( attributeMatches[i] );
165
 
166
- attr = currentShortcode.get( 'attrs' ).findWhere({
167
- attr : bits[1]
168
- });
169
 
170
- if ( attr ) {
171
- attr.set('value', bits[2]);
172
- }
173
 
 
 
 
 
 
 
 
174
  }
175
 
176
  }
@@ -191,6 +198,7 @@ var shortcodeViewConstructor = {
191
 
192
  initialize: function( options ) {
193
  this.shortcode = this.getShortcode( options );
 
194
  },
195
 
196
  getShortcode: function( options ) {
@@ -218,7 +226,9 @@ var shortcodeViewConstructor = {
218
 
219
  if ('content' in options.shortcode) {
220
  var inner_content = shortcode.get('inner_content');
221
- inner_content.set('value', options.shortcode.content)
 
 
222
  }
223
 
224
  return shortcode;
@@ -232,7 +242,7 @@ var shortcodeViewConstructor = {
232
  if ( ! this.parsed ) {
233
 
234
  wp.ajax.post( 'do_shortcode', {
235
- post_id: jQuery( '#post_ID' ).val(),
236
  shortcode: this.shortcode.formatShortcode(),
237
  nonce: shortcodeUIData.nonces.preview,
238
  }).done( function( response ) {
@@ -260,11 +270,6 @@ var shortcodeViewConstructor = {
260
  * @return string html
261
  */
262
  getHtml : function() {
263
-
264
- if ( ! this.parsed ) {
265
- this.fetch();
266
- }
267
-
268
  return this.parsed;
269
  },
270
 
1
+ var sui = require('sui-utils/sui'),
2
+ wp = require('wp'),
3
+ $ = require('jquery');
4
 
5
  /**
6
  * Generic shortcode mce view constructor.
10
 
11
  initialize: function( options ) {
12
  this.shortcodeModel = this.getShortcodeModel( this.shortcode );
13
+ this.fetch();
14
  },
15
 
16
  /**
40
  }
41
  );
42
 
43
+ if ( 'content' in options ) {
44
+ var innerContent = shortcodeModel.get('inner_content');
45
+ if ( innerContent ) {
46
+ innerContent.set('value', options.content)
47
+ }
48
  }
49
 
50
  return shortcodeModel;
51
 
52
  },
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  /**
55
  * Fetch preview.
56
  * Async. Sets this.content and calls this.render.
66
  this.fetching = true;
67
 
68
  wp.ajax.post( 'do_shortcode', {
69
+ post_id: $( '#post_ID' ).val(),
70
  shortcode: this.shortcodeModel.formatShortcode(),
71
  nonce: shortcodeUIData.nonces.preview,
72
  }).done( function( response ) {
73
+
74
+ if ( '' === response ) {
75
+ self.content = '<span class="shortcake-notice shortcake-empty">' + self.shortcodeModel.formatShortcode() + '</span>';
76
+ } else {
77
+ self.content = response;
78
+ }
79
+
80
  }).fail( function() {
81
  self.content = '<span class="shortcake-error">' + shortcodeUIData.strings.mce_view_error + '</span>';
82
  } ).always( function() {
83
  delete self.fetching;
84
+ self.render( null, true );
85
  } );
86
 
87
  }
99
 
100
  // Backwards compatability for WP pre-4.2
101
  if ( 'object' === typeof( shortcodeString ) ) {
102
+ shortcodeString = decodeURIComponent( $(shortcodeString).attr('data-wpview-text') );
103
  }
104
 
105
  currentShortcode = this.parseShortcodeString( shortcodeString );
151
 
152
  if ( matches[2] ) {
153
 
154
+ var attributeRegex = /(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/gmi;
155
+ attributeMatches = matches[2].match( attributeRegex ) || [];
156
+
157
+ // Trim whitespace from matches.
158
+ attributeMatches = attributeMatches.map( function( match ) {
159
+ return match.replace( /^\s+|\s+$/g, '' );
160
+ } );
161
 
162
  // convert attribute strings to object.
163
  for ( var i = 0; i < attributeMatches.length; i++ ) {
164
 
165
+ var bitsRegEx = /(\S+?)=(.*)/g;
166
  var bits = bitsRegEx.exec( attributeMatches[i] );
167
 
168
+ if ( bits && bits[1] ) {
 
 
169
 
170
+ attr = currentShortcode.get( 'attrs' ).findWhere({
171
+ attr : bits[1]
172
+ });
173
 
174
+ // If attribute found - set value.
175
+ // Trim quotes from beginning and end.
176
+ if ( attr ) {
177
+ attr.set( 'value', bits[2].replace( /^"|^'|"$|'$/gmi, "" ) );
178
+ }
179
+
180
+ }
181
  }
182
 
183
  }
198
 
199
  initialize: function( options ) {
200
  this.shortcode = this.getShortcode( options );
201
+ this.fetch();
202
  },
203
 
204
  getShortcode: function( options ) {
226
 
227
  if ('content' in options.shortcode) {
228
  var inner_content = shortcode.get('inner_content');
229
+ if ( inner_content ) {
230
+ inner_content.set('value', options.shortcode.content)
231
+ }
232
  }
233
 
234
  return shortcode;
242
  if ( ! this.parsed ) {
243
 
244
  wp.ajax.post( 'do_shortcode', {
245
+ post_id: $( '#post_ID' ).val(),
246
  shortcode: this.shortcode.formatShortcode(),
247
  nonce: shortcodeUIData.nonces.preview,
248
  }).done( function( response ) {
270
  * @return string html
271
  */
272
  getHtml : function() {
 
 
 
 
 
273
  return this.parsed;
274
  },
275
 
js/{utils → src/utils}/sui.js RENAMED
File without changes
js/{views → src/views}/edit-attribute-field.js RENAMED
@@ -1,5 +1,6 @@
1
- var Backbone = require('backbone');
2
- sui = require('sui-utils/sui');
 
3
 
4
  var editAttributeField = Backbone.View.extend( {
5
 
@@ -18,7 +19,42 @@ var editAttributeField = Backbone.View.extend( {
18
  },
19
 
20
  render: function() {
21
- this.$el.html( this.template( this.model.toJSON() ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  return this
23
  },
24
 
@@ -31,9 +67,9 @@ var editAttributeField = Backbone.View.extend( {
31
  updateValue: function( e ) {
32
 
33
  if ( this.model.get( 'attr' ) ) {
34
- var $el = jQuery( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
35
  } else {
36
- var $el = jQuery( this.el ).find( '[name="inner_content"]' );
37
  }
38
 
39
  if ( 'radio' === this.model.attributes.type ) {
1
+ var Backbone = require('backbone'),
2
+ sui = require('sui-utils/sui'),
3
+ $ = require('jquery');
4
 
5
  var editAttributeField = Backbone.View.extend( {
6
 
19
  },
20
 
21
  render: function() {
22
+
23
+ var data = jQuery.extend( {
24
+ id: 'shortcode-ui-' + this.model.get( 'attr' ) + '-' + this.model.cid,
25
+ }, this.model.toJSON() );
26
+
27
+ // Handle legacy custom meta.
28
+ // Can be removed in 0.4.
29
+ if ( data.placeholder ) {
30
+ data.meta.placeholder = data.placeholder;
31
+ delete data.placeholder;
32
+ }
33
+
34
+ // Convert meta JSON to attribute string.
35
+ var _meta = [];
36
+ for ( var key in data.meta ) {
37
+
38
+ // Boolean attributes can only require attribute key, not value.
39
+ if ( 'boolean' === typeof( data.meta[ key ] ) ) {
40
+
41
+ // Only set truthy boolean attributes.
42
+ if ( data.meta[ key ] ) {
43
+ _meta.push( _.escape( key ) );
44
+ }
45
+
46
+ } else {
47
+
48
+ _meta.push( _.escape( key ) + '="' + _.escape( data.meta[ key ] ) + '"' );
49
+
50
+ }
51
+
52
+ }
53
+
54
+ data.meta = _meta.join( ' ' );
55
+
56
+ this.$el.html( this.template( data ) );
57
+
58
  return this
59
  },
60
 
67
  updateValue: function( e ) {
68
 
69
  if ( this.model.get( 'attr' ) ) {
70
+ var $el = $( this.el ).find( '[name=' + this.model.get( 'attr' ) + ']' );
71
  } else {
72
+ var $el = $( this.el ).find( '[name="inner_content"]' );
73
  }
74
 
75
  if ( 'radio' === this.model.attributes.type ) {
js/{views → src/views}/edit-shortcode-form.js RENAMED
@@ -1,5 +1,6 @@
1
  var wp = require('wp'),
2
  sui = require('sui-utils/sui'),
 
3
  editAttributeField = require( 'sui-views/edit-attribute-field' );
4
 
5
  /**
@@ -13,7 +14,7 @@ var EditShortcodeForm = wp.Backbone.View.extend({
13
  var t = this;
14
 
15
  var innerContent = this.model.get( 'inner_content' );
16
- if ( typeof innerContent.attributes.type !== 'undefined' ) {
17
 
18
  // add UI for inner_content
19
  var view = new editAttributeField( {
@@ -35,17 +36,37 @@ var EditShortcodeForm = wp.Backbone.View.extend({
35
  return;
36
  }
37
 
 
 
 
 
 
 
 
38
  var viewObjName = shortcodeUIFieldData[ type ].view;
39
  var tmplName = shortcodeUIFieldData[ type ].template;
40
 
41
- var view = new sui.views[viewObjName]( { model: attr } );
42
- view.template = wp.media.template( tmplName );
43
  view.shortcode = t.model;
44
 
45
  t.views.add( '.edit-shortcode-form-fields', view );
46
 
47
  } );
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  },
50
 
51
  });
1
  var wp = require('wp'),
2
  sui = require('sui-utils/sui'),
3
+ backbone = require('backbone'),
4
  editAttributeField = require( 'sui-views/edit-attribute-field' );
5
 
6
  /**
14
  var t = this;
15
 
16
  var innerContent = this.model.get( 'inner_content' );
17
+ if ( innerContent && typeof innerContent.attributes.type !== 'undefined' ) {
18
 
19
  // add UI for inner_content
20
  var view = new editAttributeField( {
36
  return;
37
  }
38
 
39
+ var templateData = {
40
+ value: attr.get('value'),
41
+ attr_raw: {
42
+ name: attr.get('value')
43
+ }
44
+ }
45
+
46
  var viewObjName = shortcodeUIFieldData[ type ].view;
47
  var tmplName = shortcodeUIFieldData[ type ].template;
48
 
49
+ var view = new sui.views[viewObjName]( { model: attr } );
50
+ view.template = wp.media.template( tmplName );
51
  view.shortcode = t.model;
52
 
53
  t.views.add( '.edit-shortcode-form-fields', view );
54
 
55
  } );
56
 
57
+ if ( 0 == this.model.get( 'attrs' ).length && ( ! innerContent || typeof innerContent == 'undefined' ) ) {
58
+ var messageView = new Backbone.View({
59
+ tagName: 'div',
60
+ className: 'notice updated',
61
+ });
62
+ messageView.render = function() {
63
+ this.$el.append( '<p>' );
64
+ this.$el.find('p').text( shortcodeUIData.strings.media_frame_no_attributes_message );
65
+ return this;
66
+ };
67
+ t.views.add( '.edit-shortcode-form-fields', messageView );
68
+ }
69
+
70
  },
71
 
72
  });
js/{views → src/views}/insert-shortcode-list-item.js RENAMED
File without changes
js/{views → src/views}/insert-shortcode-list.js RENAMED
File without changes
js/{views → src/views}/media-frame.js RENAMED
@@ -1,4 +1,5 @@
1
  var wp = require('wp'),
 
2
  MediaController = require('sui-controllers/media-controller'),
3
  Shortcode_UI = require('./shortcode-ui'),
4
  Toolbar = require('./media-toolbar');
@@ -25,17 +26,17 @@ var mediaFrame = postMediaFrame.extend( {
25
  };
26
 
27
  if ( 'currentShortcode' in this.options ) {
28
- opts.title = shortcodeUIData.strings.media_frame_menu_update_label;
29
  }
30
 
31
- var controller = new MediaController( opts );
32
 
33
  if ( 'currentShortcode' in this.options ) {
34
- controller.props.set( 'currentShortcode', arguments[0].currentShortcode );
35
- controller.props.set( 'action', 'update' );
36
  }
37
 
38
- this.states.add([ controller]);
39
 
40
  this.on( 'content:render:' + id + '-content-insert', _.bind( this.contentRender, this, 'shortcode-ui', 'insert' ) );
41
  this.on( 'toolbar:create:' + id + '-toolbar', this.toolbarCreate, this );
@@ -44,6 +45,19 @@ var mediaFrame = postMediaFrame.extend( {
44
 
45
  },
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  contentRender : function( id, tab ) {
48
  this.content.set(
49
  new Shortcode_UI( {
1
  var wp = require('wp'),
2
+ $ = require('jquery'),
3
  MediaController = require('sui-controllers/media-controller'),
4
  Shortcode_UI = require('./shortcode-ui'),
5
  Toolbar = require('./media-toolbar');
26
  };
27
 
28
  if ( 'currentShortcode' in this.options ) {
29
+ opts.title = shortcodeUIData.strings.media_frame_menu_update_label.replace( /%s/, this.options.currentShortcode.attributes.label );
30
  }
31
 
32
+ this.mediaController = new MediaController( opts );
33
 
34
  if ( 'currentShortcode' in this.options ) {
35
+ this.mediaController.props.set( 'currentShortcode', arguments[0].currentShortcode );
36
+ this.mediaController.props.set( 'action', 'update' );
37
  }
38
 
39
+ this.states.add([ this.mediaController ]);
40
 
41
  this.on( 'content:render:' + id + '-content-insert', _.bind( this.contentRender, this, 'shortcode-ui', 'insert' ) );
42
  this.on( 'toolbar:create:' + id + '-toolbar', this.toolbarCreate, this );
45
 
46
  },
47
 
48
+ events: function() {
49
+ return _.extend( {}, postMediaFrame.prototype.events, {
50
+ 'click .media-menu-item' : 'resetMediaController',
51
+ } );
52
+ },
53
+
54
+ resetMediaController: function( event ) {
55
+ if ( this.state().props.get('currentShortcode') ) {
56
+ this.mediaController.reset();
57
+ this.contentRender( 'shortcode-ui', 'insert' );
58
+ }
59
+ },
60
+
61
  contentRender : function( id, tab ) {
62
  this.content.set(
63
  new Shortcode_UI( {
js/{views → src/views}/media-toolbar.js RENAMED
File without changes
js/{views → src/views}/search-shortcode.js RENAMED
File without changes
js/{views → src/views}/shortcode-preview.js RENAMED
@@ -1,4 +1,5 @@
1
- var Backbone = require('backbone');
 
2
 
3
  /**
4
  * Preview of rendered shortcode.
@@ -61,8 +62,10 @@ var ShortcodePreview = Backbone.View.extend({
61
 
62
  _.defaults( params || {}, { 'head': '', 'body': '', 'body_classes': 'shortcake shortcake-preview' });
63
 
64
- $iframe = jQuery( '<iframe/>', {
65
- src: tinymce.Env.ie ? 'javascript:""' : '',
 
 
66
  frameBorder: '0',
67
  allowTransparency: 'true',
68
  scrolling: 'no',
@@ -76,10 +79,10 @@ var ShortcodePreview = Backbone.View.extend({
76
  */
77
  $iframe.load( function() {
78
 
79
- self.autoresizeIframe( jQuery(this) );
80
 
81
- var head = jQuery(this).contents().find('head'),
82
- body = jQuery(this).contents().find('body');
83
 
84
  head.html( params.head );
85
  body.html( params.body );
@@ -95,7 +98,7 @@ var ShortcodePreview = Backbone.View.extend({
95
  * Watch for mutations in iFrame content.
96
  * resize iFrame height on change.
97
  *
98
- * @param jQuery object $iframe
99
  */
100
  autoresizeIframe: function( $iframe ) {
101
 
@@ -142,7 +145,7 @@ var ShortcodePreview = Backbone.View.extend({
142
  fetchShortcode: function( callback ) {
143
 
144
  wp.ajax.post( 'do_shortcode', {
145
- post_id: jQuery( '#post_ID' ).val(),
146
  shortcode: this.model.formatShortcode(),
147
  nonce: shortcodeUIData.nonces.preview,
148
  }).done( function( response ) {
@@ -162,7 +165,8 @@ var ShortcodePreview = Backbone.View.extend({
162
  getEditorStyles: function() {
163
  var styles = {};
164
 
165
- _.each( tinymce.editors, function( editor ) {
 
166
  _.each( editor.dom.$( 'link[rel="stylesheet"]', editor.getDoc().head ), function( link ) {
167
  var href;
168
  ( href = link.href ) && ( styles[href] = true ); // Poor man's de-duping.
@@ -170,7 +174,7 @@ var ShortcodePreview = Backbone.View.extend({
170
  });
171
 
172
  styles = _.map( _.keys( styles ), function( href ) {
173
- return jQuery( '<link rel="stylesheet" type="text/css">' ).attr( 'href', href )[0].outerHTML;
174
  });
175
 
176
  return styles;
1
+ var Backbone = require('backbone'),
2
+ $ = require('jquery');
3
 
4
  /**
5
  * Preview of rendered shortcode.
62
 
63
  _.defaults( params || {}, { 'head': '', 'body': '', 'body_classes': 'shortcake shortcake-preview' });
64
 
65
+ var isIE = typeof tinymce != 'undefined' ? tinymce.Env.ie : false;
66
+
67
+ $iframe = $( '<iframe/>', {
68
+ src: isIE ? 'javascript:""' : '',
69
  frameBorder: '0',
70
  allowTransparency: 'true',
71
  scrolling: 'no',
79
  */
80
  $iframe.load( function() {
81
 
82
+ self.autoresizeIframe( $(this) );
83
 
84
+ var head = $(this).contents().find('head'),
85
+ body = $(this).contents().find('body');
86
 
87
  head.html( params.head );
88
  body.html( params.body );
98
  * Watch for mutations in iFrame content.
99
  * resize iFrame height on change.
100
  *
101
+ * @param $ object $iframe
102
  */
103
  autoresizeIframe: function( $iframe ) {
104
 
145
  fetchShortcode: function( callback ) {
146
 
147
  wp.ajax.post( 'do_shortcode', {
148
+ post_id: $( '#post_ID' ).val(),
149
  shortcode: this.model.formatShortcode(),
150
  nonce: shortcodeUIData.nonces.preview,
151
  }).done( function( response ) {
165
  getEditorStyles: function() {
166
  var styles = {};
167
 
168
+ var editors = typeof tinymce != 'undefined' ? tinymce.editors : [];
169
+ _.each( editors, function( editor ) {
170
  _.each( editor.dom.$( 'link[rel="stylesheet"]', editor.getDoc().head ), function( link ) {
171
  var href;
172
  ( href = link.href ) && ( styles[href] = true ); // Poor man's de-duping.
174
  });
175
 
176
  styles = _.map( _.keys( styles ), function( href ) {
177
+ return $( '<link rel="stylesheet" type="text/css">' ).attr( 'href', href )[0].outerHTML;
178
  });
179
 
180
  return styles;
js/{views → src/views}/shortcode-ui.js RENAMED
@@ -5,8 +5,8 @@ var Backbone = require('backbone'),
5
  EditShortcodeForm = require('sui-views/edit-shortcode-form'),
6
  Toolbar = require('sui-views/media-toolbar'),
7
  SearchShortcode = require('sui-views/search-shortcode'),
8
- sui = require('sui-utils/sui');
9
- jQuery = require('jquery');
10
 
11
  var Shortcode_UI = Backbone.View.extend({
12
 
@@ -106,7 +106,7 @@ var Shortcode_UI = Backbone.View.extend({
106
 
107
  select: function(e) {
108
  this.controller.props.set( 'action', 'insert' );
109
- var target = jQuery(e.currentTarget).closest( '.shortcode-list-item' );
110
  var shortcode = sui.shortcodes.findWhere( { shortcode_tag: target.attr( 'data-shortcode' ) } );
111
 
112
  if ( ! shortcode ) {
5
  EditShortcodeForm = require('sui-views/edit-shortcode-form'),
6
  Toolbar = require('sui-views/media-toolbar'),
7
  SearchShortcode = require('sui-views/search-shortcode'),
8
+ sui = require('sui-utils/sui'),
9
+ $ = require('jquery');
10
 
11
  var Shortcode_UI = Backbone.View.extend({
12
 
106
 
107
  select: function(e) {
108
  this.controller.props.set( 'action', 'insert' );
109
+ var target = $(e.currentTarget).closest( '.shortcode-list-item' );
110
  var shortcode = sui.shortcodes.findWhere( { shortcode_tag: target.attr( 'data-shortcode' ) } );
111
 
112
  if ( ! shortcode ) {
js/{views → src/views}/tabbed-view.js RENAMED
@@ -1,5 +1,6 @@
1
  var Backbone = require('backbone');
2
  var sui = require('sui-utils/sui');
 
3
 
4
  /**
5
  * Abstraction to manage tabbed content. Tab parameters (e.g., label) along with
@@ -72,7 +73,7 @@ var TabbedView = Backbone.View.extend({
72
  event.stopPropagation();
73
  event.preventDefault();
74
 
75
- var target = jQuery(event.currentTarget).attr('data-target');
76
 
77
  this.select(target);
78
  },
1
  var Backbone = require('backbone');
2
  var sui = require('sui-utils/sui');
3
+ var $ = require('jquery');
4
 
5
  /**
6
  * Abstraction to manage tabbed content. Tab parameters (e.g., label) along with
73
  event.stopPropagation();
74
  event.preventDefault();
75
 
76
+ var target = $(event.currentTarget).attr('data-target');
77
 
78
  this.select(target);
79
  },
languages/shortcode-ui-nl_NL.mo ADDED
Binary file
languages/shortcode-ui-nl_NL.po ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Shortcode UI vv0.3-beta1\n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: \n"
6
+ "PO-Revision-Date: 2015-04-27 19:20:49+0000\n"
7
+ "Last-Translator: youssou <youssouhaagsman@gmail.com>\n"
8
+ "Language-Team: \n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
13
+ "X-Generator: CSL v1.x\n"
14
+ "X-Poedit-Language: Dutch\n"
15
+ "X-Poedit-Country: NETHERLANDS\n"
16
+ "X-Poedit-SourceCharset: utf-8\n"
17
+ "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n"
18
+ "X-Poedit-Basepath: ../\n"
19
+ "X-Poedit-Bookmarks: \n"
20
+ "X-Poedit-SearchPath-0: .\n"
21
+ "X-Textdomain-Support: yes"
22
+
23
+ #: inc/class-shortcode-ui.php:40
24
+ #@ shortcode-ui
25
+ msgid "Inner Content"
26
+ msgstr "Ingesloten inhoud"
27
+
28
+ #: inc/class-shortcode-ui.php:105
29
+ #: inc/class-shortcode-ui.php:106
30
+ #@ shortcode-ui
31
+ msgid "Insert Post Element"
32
+ msgstr "Berichtelement invoegen"
33
+
34
+ #: inc/class-shortcode-ui.php:107
35
+ #, php-format
36
+ #@ shortcode-ui
37
+ msgid "%s Details"
38
+ msgstr "%s details"
39
+
40
+ #: inc/class-shortcode-ui.php:108
41
+ #@ shortcode-ui
42
+ msgid "Insert Element"
43
+ msgstr "Element invoegen"
44
+
45
+ #: inc/class-shortcode-ui.php:109
46
+ #@ shortcode-ui
47
+ msgid "Update"
48
+ msgstr "Updaten"
49
+
50
+ #: inc/class-shortcode-ui.php:110
51
+ #@ shortcode-ui
52
+ msgid "There are no attributes to configure for this Post Element."
53
+ msgstr "Er zijn geen attributen die bewerkt kunnen worden voor dit berichtelement."
54
+
55
+ #: inc/class-shortcode-ui.php:111
56
+ #@ shortcode-ui
57
+ msgid "Edit"
58
+ msgstr "Bewerken"
59
+
60
+ #: inc/class-shortcode-ui.php:112
61
+ #@ shortcode-ui
62
+ msgid "Preview"
63
+ msgstr "Voorbeeld"
64
+
65
+ #: inc/class-shortcode-ui.php:113
66
+ #@ shortcode-ui
67
+ msgid "Failed to load preview"
68
+ msgstr "Laden van voorbeeld mislukt"
69
+
70
+ #: inc/class-shortcode-ui.php:114
71
+ #@ shortcode-ui
72
+ msgid "Search"
73
+ msgstr "Zoeken"
74
+
75
+ #: inc/class-shortcode-ui.php:115
76
+ #@ shortcode-ui
77
+ msgid "Insert Content"
78
+ msgstr "Inhoud invoegen"
79
+
80
+ #: inc/class-shortcode-ui.php:209
81
+ #@ shortcode-ui
82
+ msgid "Something's rotten in the state of Denmark"
83
+ msgstr "Iets is er rot in Denemarkens staat"
84
+
85
+ #: inc/fields/class-field-attachment.php:47
86
+ #: inc/fields/class-field-attachment.php:48
87
+ #@ shortcode-ui
88
+ msgid "Select Attachment"
89
+ msgstr "Selecteer bijlage"
90
+
91
+ #: inc/templates/edit-form.tpl.php:3
92
+ #@ shortcode-ui
93
+ msgid "Back to list"
94
+ msgstr "Terug naar lijst"
95
+
languages/shortcode-ui.pot CHANGED
@@ -2,9 +2,9 @@
2
  # This file is distributed under the GPL v2 or later.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Shortcode UI v0.2.0-dev\n"
6
  "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/shortcode-ui\n"
7
- "POT-Creation-Date: 2015-03-18 14:20:37+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
@@ -24,43 +24,51 @@ msgstr ""
24
  "X-Poedit-Bookmarks: \n"
25
  "X-Textdomain-Support: yes\n"
26
 
27
- #: inc/class-shortcode-ui.php:50
28
  msgid "Inner Content"
29
  msgstr ""
30
 
31
- #: inc/class-shortcode-ui.php:123 inc/class-shortcode-ui.php:124
32
  msgid "Insert Post Element"
33
  msgstr ""
34
 
35
- #: inc/class-shortcode-ui.php:125
36
- msgid "Post Element Details"
37
  msgstr ""
38
 
39
- #: inc/class-shortcode-ui.php:126
40
  msgid "Insert Element"
41
  msgstr ""
42
 
43
- #: inc/class-shortcode-ui.php:127
44
  msgid "Update"
45
  msgstr ""
46
 
47
- #: inc/class-shortcode-ui.php:128
 
 
 
 
48
  msgid "Edit"
49
  msgstr ""
50
 
51
- #: inc/class-shortcode-ui.php:129
52
  msgid "Preview"
53
  msgstr ""
54
 
55
- #: inc/class-shortcode-ui.php:130
56
  msgid "Failed to load preview"
57
  msgstr ""
58
 
59
- #: inc/class-shortcode-ui.php:131
60
  msgid "Search"
61
  msgstr ""
62
 
63
- #: inc/class-shortcode-ui.php:218
 
 
 
 
64
  msgid "Something's rotten in the state of Denmark"
65
  msgstr ""
66
 
2
  # This file is distributed under the GPL v2 or later.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Shortcode UI v0.3-alpha\n"
6
  "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/shortcode-ui\n"
7
+ "POT-Creation-Date: 2015-04-22 16:48:40+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
24
  "X-Poedit-Bookmarks: \n"
25
  "X-Textdomain-Support: yes\n"
26
 
27
+ #: inc/class-shortcode-ui.php:40
28
  msgid "Inner Content"
29
  msgstr ""
30
 
31
+ #: inc/class-shortcode-ui.php:101 inc/class-shortcode-ui.php:102
32
  msgid "Insert Post Element"
33
  msgstr ""
34
 
35
+ #: inc/class-shortcode-ui.php:103
36
+ msgid "%s Details"
37
  msgstr ""
38
 
39
+ #: inc/class-shortcode-ui.php:104
40
  msgid "Insert Element"
41
  msgstr ""
42
 
43
+ #: inc/class-shortcode-ui.php:105
44
  msgid "Update"
45
  msgstr ""
46
 
47
+ #: inc/class-shortcode-ui.php:106
48
+ msgid "There are no attributes to configure for this Post Element."
49
+ msgstr ""
50
+
51
+ #: inc/class-shortcode-ui.php:107
52
  msgid "Edit"
53
  msgstr ""
54
 
55
+ #: inc/class-shortcode-ui.php:108
56
  msgid "Preview"
57
  msgstr ""
58
 
59
+ #: inc/class-shortcode-ui.php:109
60
  msgid "Failed to load preview"
61
  msgstr ""
62
 
63
+ #: inc/class-shortcode-ui.php:110
64
  msgid "Search"
65
  msgstr ""
66
 
67
+ #: inc/class-shortcode-ui.php:111
68
+ msgid "Insert Content"
69
+ msgstr ""
70
+
71
+ #: inc/class-shortcode-ui.php:205
72
  msgid "Something's rotten in the state of Denmark"
73
  msgstr ""
74
 
lib/select2/select2-spinner.gif ADDED
Binary file
lib/select2/select2.css ADDED
@@ -0,0 +1,704 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014
3
+ */
4
+ .select2-container {
5
+ margin: 0;
6
+ position: relative;
7
+ display: inline-block;
8
+ /* inline-block for ie7 */
9
+ zoom: 1;
10
+ *display: inline;
11
+ vertical-align: middle;
12
+ }
13
+
14
+ .select2-container,
15
+ .select2-drop,
16
+ .select2-search,
17
+ .select2-search input {
18
+ /*
19
+ Force border-box so that % widths fit the parent
20
+ container without overlap because of margin/padding.
21
+ More Info : http://www.quirksmode.org/css/box.html
22
+ */
23
+ -webkit-box-sizing: border-box; /* webkit */
24
+ -moz-box-sizing: border-box; /* firefox */
25
+ box-sizing: border-box; /* css3 */
26
+ }
27
+
28
+ .select2-container .select2-choice {
29
+ display: block;
30
+ height: 26px;
31
+ padding: 0 0 0 8px;
32
+ overflow: hidden;
33
+ position: relative;
34
+
35
+ border: 1px solid #aaa;
36
+ white-space: nowrap;
37
+ line-height: 26px;
38
+ color: #444;
39
+ text-decoration: none;
40
+
41
+ border-radius: 4px;
42
+
43
+ background-clip: padding-box;
44
+
45
+ -webkit-touch-callout: none;
46
+ -webkit-user-select: none;
47
+ -moz-user-select: none;
48
+ -ms-user-select: none;
49
+ user-select: none;
50
+
51
+ background-color: #fff;
52
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff));
53
+ background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%);
54
+ background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%);
55
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
56
+ background-image: linear-gradient(to top, #eee 0%, #fff 50%);
57
+ }
58
+
59
+ html[dir="rtl"] .select2-container .select2-choice {
60
+ padding: 0 8px 0 0;
61
+ }
62
+
63
+ .select2-container.select2-drop-above .select2-choice {
64
+ border-bottom-color: #aaa;
65
+
66
+ border-radius: 0 0 4px 4px;
67
+
68
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff));
69
+ background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%);
70
+ background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%);
71
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);
72
+ background-image: linear-gradient(to bottom, #eee 0%, #fff 90%);
73
+ }
74
+
75
+ .select2-container.select2-allowclear .select2-choice .select2-chosen {
76
+ margin-right: 42px;
77
+ }
78
+
79
+ .select2-container .select2-choice > .select2-chosen {
80
+ margin-right: 26px;
81
+ display: block;
82
+ overflow: hidden;
83
+
84
+ white-space: nowrap;
85
+
86
+ text-overflow: ellipsis;
87
+ float: none;
88
+ width: auto;
89
+ }
90
+
91
+ html[dir="rtl"] .select2-container .select2-choice > .select2-chosen {
92
+ margin-left: 26px;
93
+ margin-right: 0;
94
+ }
95
+
96
+ .select2-container .select2-choice abbr {
97
+ display: none;
98
+ width: 12px;
99
+ height: 12px;
100
+ position: absolute;
101
+ right: 24px;
102
+ top: 8px;
103
+
104
+ font-size: 1px;
105
+ text-decoration: none;
106
+
107
+ border: 0;
108
+ background: url('select2.png') right top no-repeat;
109
+ cursor: pointer;
110
+ outline: 0;
111
+ }
112
+
113
+ .select2-container.select2-allowclear .select2-choice abbr {
114
+ display: inline-block;
115
+ }
116
+
117
+ .select2-container .select2-choice abbr:hover {
118
+ background-position: right -11px;
119
+ cursor: pointer;
120
+ }
121
+
122
+ .select2-drop-mask {
123
+ border: 0;
124
+ margin: 0;
125
+ padding: 0;
126
+ position: fixed;
127
+ left: 0;
128
+ top: 0;
129
+ min-height: 100%;
130
+ min-width: 100%;
131
+ height: auto;
132
+ width: auto;
133
+ opacity: 0;
134
+ z-index: 9998;
135
+ /* styles required for IE to work */
136
+ background-color: #fff;
137
+ filter: alpha(opacity=0);
138
+ }
139
+
140
+ .select2-drop {
141
+ width: 100%;
142
+ margin-top: -1px;
143
+ position: absolute;
144
+ z-index: 9999;
145
+ top: 100%;
146
+
147
+ background: #fff;
148
+ color: #000;
149
+ border: 1px solid #aaa;
150
+ border-top: 0;
151
+
152
+ border-radius: 0 0 4px 4px;
153
+
154
+ -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
155
+ box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
156
+ }
157
+
158
+ .select2-drop.select2-drop-above {
159
+ margin-top: 1px;
160
+ border-top: 1px solid #aaa;
161
+ border-bottom: 0;
162
+
163
+ border-radius: 4px 4px 0 0;
164
+
165
+ -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
166
+ box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
167
+ }
168
+
169
+ .select2-drop-active {
170
+ border: 1px solid #5897fb;
171
+ border-top: none;
172
+ }
173
+
174
+ .select2-drop.select2-drop-above.select2-drop-active {
175
+ border-top: 1px solid #5897fb;
176
+ }
177
+
178
+ .select2-drop-auto-width {
179
+ border-top: 1px solid #aaa;
180
+ width: auto;
181
+ }
182
+
183
+ .select2-drop-auto-width .select2-search {
184
+ padding-top: 4px;
185
+ }
186
+
187
+ .select2-container .select2-choice .select2-arrow {
188
+ display: inline-block;
189
+ width: 18px;
190
+ height: 100%;
191
+ position: absolute;
192
+ right: 0;
193
+ top: 0;
194
+
195
+ border-left: 1px solid #aaa;
196
+ border-radius: 0 4px 4px 0;
197
+
198
+ background-clip: padding-box;
199
+
200
+ background: #ccc;
201
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
202
+ background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
203
+ background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
204
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
205
+ background-image: linear-gradient(to top, #ccc 0%, #eee 60%);
206
+ }
207
+
208
+ html[dir="rtl"] .select2-container .select2-choice .select2-arrow {
209
+ left: 0;
210
+ right: auto;
211
+
212
+ border-left: none;
213
+ border-right: 1px solid #aaa;
214
+ border-radius: 4px 0 0 4px;
215
+ }
216
+
217
+ .select2-container .select2-choice .select2-arrow b {
218
+ display: block;
219
+ width: 100%;
220
+ height: 100%;
221
+ background: url('select2.png') no-repeat 0 1px;
222
+ }
223
+
224
+ html[dir="rtl"] .select2-container .select2-choice .select2-arrow b {
225
+ background-position: 2px 1px;
226
+ }
227
+
228
+ .select2-search {
229
+ display: inline-block;
230
+ width: 100%;
231
+ min-height: 26px;
232
+ margin: 0;
233
+ padding-left: 4px;
234
+ padding-right: 4px;
235
+
236
+ position: relative;
237
+ z-index: 10000;
238
+
239
+ white-space: nowrap;
240
+ }
241
+
242
+ .select2-search input {
243
+ width: 100%;
244
+ height: auto !important;
245
+ min-height: 26px;
246
+ padding: 4px 20px 4px 5px;
247
+ margin: 0;
248
+
249
+ outline: 0;
250
+ font-family: sans-serif;
251
+ font-size: 1em;
252
+
253
+ border: 1px solid #aaa;
254
+ border-radius: 0;
255
+
256
+ -webkit-box-shadow: none;
257
+ box-shadow: none;
258
+
259
+ background: #fff url('select2.png') no-repeat 100% -22px;
260
+ background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
261
+ background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
262
+ background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
263
+ background: url('select2.png') no-repeat 100% -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
264
+ }
265
+
266
+ html[dir="rtl"] .select2-search input {
267
+ padding: 4px 5px 4px 20px;
268
+
269
+ background: #fff url('select2.png') no-repeat -37px -22px;
270
+ background: url('select2.png') no-repeat -37px -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
271
+ background: url('select2.png') no-repeat -37px -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
272
+ background: url('select2.png') no-repeat -37px -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
273
+ background: url('select2.png') no-repeat -37px -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
274
+ }
275
+
276
+ .select2-drop.select2-drop-above .select2-search input {
277
+ margin-top: 4px;
278
+ }
279
+
280
+ .select2-search input.select2-active {
281
+ background: #fff url('select2-spinner.gif') no-repeat 100%;
282
+ background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
283
+ background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
284
+ background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
285
+ background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
286
+ }
287
+
288
+ .select2-container-active .select2-choice,
289
+ .select2-container-active .select2-choices {
290
+ border: 1px solid #5897fb;
291
+ outline: none;
292
+
293
+ -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
294
+ box-shadow: 0 0 5px rgba(0, 0, 0, .3);
295
+ }
296
+
297
+ .select2-dropdown-open .select2-choice {
298
+ border-bottom-color: transparent;
299
+ -webkit-box-shadow: 0 1px 0 #fff inset;
300
+ box-shadow: 0 1px 0 #fff inset;
301
+
302
+ border-bottom-left-radius: 0;
303
+ border-bottom-right-radius: 0;
304
+
305
+ background-color: #eee;
306
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee));
307
+ background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%);
308
+ background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%);
309
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
310
+ background-image: linear-gradient(to top, #fff 0%, #eee 50%);
311
+ }
312
+
313
+ .select2-dropdown-open.select2-drop-above .select2-choice,
314
+ .select2-dropdown-open.select2-drop-above .select2-choices {
315
+ border: 1px solid #5897fb;
316
+ border-top-color: transparent;
317
+
318
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee));
319
+ background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%);
320
+ background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%);
321
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
322
+ background-image: linear-gradient(to bottom, #fff 0%, #eee 50%);
323
+ }
324
+
325
+ .select2-dropdown-open .select2-choice .select2-arrow {
326
+ background: transparent;
327
+ border-left: none;
328
+ filter: none;
329
+ }
330
+ html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow {
331
+ border-right: none;
332
+ }
333
+
334
+ .select2-dropdown-open .select2-choice .select2-arrow b {
335
+ background-position: -18px 1px;
336
+ }
337
+
338
+ html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow b {
339
+ background-position: -16px 1px;
340
+ }
341
+
342
+ .select2-hidden-accessible {
343
+ border: 0;
344
+ clip: rect(0 0 0 0);
345
+ height: 1px;
346
+ margin: -1px;
347
+ overflow: hidden;
348
+ padding: 0;
349
+ position: absolute;
350
+ width: 1px;
351
+ }
352
+
353
+ /* results */
354
+ .select2-results {
355
+ max-height: 200px;
356
+ padding: 0 0 0 4px;
357
+ margin: 4px 4px 4px 0;
358
+ position: relative;
359
+ overflow-x: hidden;
360
+ overflow-y: auto;
361
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
362
+ }
363
+
364
+ html[dir="rtl"] .select2-results {
365
+ padding: 0 4px 0 0;
366
+ margin: 4px 0 4px 4px;
367
+ }
368
+
369
+ .select2-results ul.select2-result-sub {
370
+ margin: 0;
371
+ padding-left: 0;
372
+ }
373
+
374
+ .select2-results li {
375
+ list-style: none;
376
+ display: list-item;
377
+ background-image: none;
378
+ }
379
+
380
+ .select2-results li.select2-result-with-children > .select2-result-label {
381
+ font-weight: bold;
382
+ }
383
+
384
+ .select2-results .select2-result-label {
385
+ padding: 3px 7px 4px;
386
+ margin: 0;
387
+ cursor: pointer;
388
+
389
+ min-height: 1em;
390
+
391
+ -webkit-touch-callout: none;
392
+ -webkit-user-select: none;
393
+ -moz-user-select: none;
394
+ -ms-user-select: none;
395
+ user-select: none;
396
+ }
397
+
398
+ .select2-results-dept-1 .select2-result-label { padding-left: 20px }
399
+ .select2-results-dept-2 .select2-result-label { padding-left: 40px }
400
+ .select2-results-dept-3 .select2-result-label { padding-left: 60px }
401
+ .select2-results-dept-4 .select2-result-label { padding-left: 80px }
402
+ .select2-results-dept-5 .select2-result-label { padding-left: 100px }
403
+ .select2-results-dept-6 .select2-result-label { padding-left: 110px }
404
+ .select2-results-dept-7 .select2-result-label { padding-left: 120px }
405
+
406
+ .select2-results .select2-highlighted {
407
+ background: #3875d7;
408
+ color: #fff;
409
+ }
410
+
411
+ .select2-results li em {
412
+ background: #feffde;
413
+ font-style: normal;
414
+ }
415
+
416
+ .select2-results .select2-highlighted em {
417
+ background: transparent;
418
+ }
419
+
420
+ .select2-results .select2-highlighted ul {
421
+ background: #fff;
422
+ color: #000;
423
+ }
424
+
425
+ .select2-results .select2-no-results,
426
+ .select2-results .select2-searching,
427
+ .select2-results .select2-ajax-error,
428
+ .select2-results .select2-selection-limit {
429
+ background: #f4f4f4;
430
+ display: list-item;
431
+ padding-left: 5px;
432
+ }
433
+
434
+ /*
435
+ disabled look for disabled choices in the results dropdown
436
+ */
437
+ .select2-results .select2-disabled.select2-highlighted {
438
+ color: #666;
439
+ background: #f4f4f4;
440
+ display: list-item;
441
+ cursor: default;
442
+ }
443
+ .select2-results .select2-disabled {
444
+ background: #f4f4f4;
445
+ display: list-item;
446
+ cursor: default;
447
+ }
448
+
449
+ .select2-results .select2-selected {
450
+ display: none;
451
+ }
452
+
453
+ .select2-more-results.select2-active {
454
+ background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%;
455
+ }
456
+
457
+ .select2-results .select2-ajax-error {
458
+ background: rgba(255, 50, 50, .2);
459
+ }
460
+
461
+ .select2-more-results {
462
+ background: #f4f4f4;
463
+ display: list-item;
464
+ }
465
+
466
+ /* disabled styles */
467
+
468
+ .select2-container.select2-container-disabled .select2-choice {
469
+ background-color: #f4f4f4;
470
+ background-image: none;
471
+ border: 1px solid #ddd;
472
+ cursor: default;
473
+ }
474
+
475
+ .select2-container.select2-container-disabled .select2-choice .select2-arrow {
476
+ background-color: #f4f4f4;
477
+ background-image: none;
478
+ border-left: 0;
479
+ }
480
+
481
+ .select2-container.select2-container-disabled .select2-choice abbr {
482
+ display: none;
483
+ }
484
+
485
+
486
+ /* multiselect */
487
+
488
+ .select2-container-multi .select2-choices {
489
+ height: auto !important;
490
+ height: 1%;
491
+ margin: 0;
492
+ padding: 0 5px 0 0;
493
+ position: relative;
494
+
495
+ border: 1px solid #aaa;
496
+ cursor: text;
497
+ overflow: hidden;
498
+
499
+ background-color: #fff;
500
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff));
501
+ background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%);
502
+ background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%);
503
+ background-image: linear-gradient(to bottom, #eee 1%, #fff 15%);
504
+ }
505
+
506
+ html[dir="rtl"] .select2-container-multi .select2-choices {
507
+ padding: 0 0 0 5px;
508
+ }
509
+
510
+ .select2-locked {
511
+ padding: 3px 5px 3px 5px !important;
512
+ }
513
+
514
+ .select2-container-multi .select2-choices {
515
+ min-height: 26px;
516
+ }
517
+
518
+ .select2-container-multi.select2-container-active .select2-choices {
519
+ border: 1px solid #5897fb;
520
+ outline: none;
521
+
522
+ -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
523
+ box-shadow: 0 0 5px rgba(0, 0, 0, .3);
524
+ }
525
+ .select2-container-multi .select2-choices li {
526
+ float: left;
527
+ list-style: none;
528
+ }
529
+ html[dir="rtl"] .select2-container-multi .select2-choices li
530
+ {
531
+ float: right;
532
+ }
533
+ .select2-container-multi .select2-choices .select2-search-field {
534
+ margin: 0;
535
+ padding: 0;
536
+ white-space: nowrap;
537
+ }
538
+
539
+ .select2-container-multi .select2-choices .select2-search-field input {
540
+ padding: 5px;
541
+ margin: 1px 0;
542
+
543
+ font-family: sans-serif;
544
+ font-size: 100%;
545
+ color: #666;
546
+ outline: 0;
547
+ border: 0;
548
+ -webkit-box-shadow: none;
549
+ box-shadow: none;
550
+ background: transparent !important;
551
+ }
552
+
553
+ .select2-container-multi .select2-choices .select2-search-field input.select2-active {
554
+ background: #fff url('select2-spinner.gif') no-repeat 100% !important;
555
+ }
556
+
557
+ .select2-default {
558
+ color: #999 !important;
559
+ }
560
+
561
+ .select2-container-multi .select2-choices .select2-search-choice {
562
+ padding: 3px 5px 3px 18px;
563
+ margin: 3px 0 3px 5px;
564
+ position: relative;
565
+
566
+ line-height: 13px;
567
+ color: #333;
568
+ cursor: default;
569
+ border: 1px solid #aaaaaa;
570
+
571
+ border-radius: 3px;
572
+
573
+ -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
574
+ box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
575
+
576
+ background-clip: padding-box;
577
+
578
+ -webkit-touch-callout: none;
579
+ -webkit-user-select: none;
580
+ -moz-user-select: none;
581
+ -ms-user-select: none;
582
+ user-select: none;
583
+
584
+ background-color: #e4e4e4;
585
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0);
586
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee));
587
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
588
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
589
+ background-image: linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
590
+ }
591
+ html[dir="rtl"] .select2-container-multi .select2-choices .select2-search-choice
592
+ {
593
+ margin: 3px 5px 3px 0;
594
+ padding: 3px 18px 3px 5px;
595
+ }
596
+ .select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
597
+ cursor: default;
598
+ }
599
+ .select2-container-multi .select2-choices .select2-search-choice-focus {
600
+ background: #d4d4d4;
601
+ }
602
+
603
+ .select2-search-choice-close {
604
+ display: block;
605
+ width: 12px;
606
+ height: 13px;
607
+ position: absolute;
608
+ right: 3px;
609
+ top: 4px;
610
+
611
+ font-size: 1px;
612
+ outline: none;
613
+ background: url('select2.png') right top no-repeat;
614
+ }
615
+ html[dir="rtl"] .select2-search-choice-close {
616
+ right: auto;
617
+ left: 3px;
618
+ }
619
+
620
+ .select2-container-multi .select2-search-choice-close {
621
+ left: 3px;
622
+ }
623
+
624
+ html[dir="rtl"] .select2-container-multi .select2-search-choice-close {
625
+ left: auto;
626
+ right: 2px;
627
+ }
628
+
629
+ .select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
630
+ background-position: right -11px;
631
+ }
632
+ .select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
633
+ background-position: right -11px;
634
+ }
635
+
636
+ /* disabled styles */
637
+ .select2-container-multi.select2-container-disabled .select2-choices {
638
+ background-color: #f4f4f4;
639
+ background-image: none;
640
+ border: 1px solid #ddd;
641
+ cursor: default;
642
+ }
643
+
644
+ .select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
645
+ padding: 3px 5px 3px 5px;
646
+ border: 1px solid #ddd;
647
+ background-image: none;
648
+ background-color: #f4f4f4;
649
+ }
650
+
651
+ .select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
652
+ background: none;
653
+ }
654
+ /* end multiselect */
655
+
656
+
657
+ .select2-result-selectable .select2-match,
658
+ .select2-result-unselectable .select2-match {
659
+ text-decoration: underline;
660
+ }
661
+
662
+ .select2-offscreen, .select2-offscreen:focus {
663
+ clip: rect(0 0 0 0) !important;
664
+ width: 1px !important;
665
+ height: 1px !important;
666
+ border: 0 !important;
667
+ margin: 0 !important;
668
+ padding: 0 !important;
669
+ overflow: hidden !important;
670
+ position: absolute !important;
671
+ outline: 0 !important;
672
+ left: 0px !important;
673
+ top: 0px !important;
674
+ }
675
+
676
+ .select2-display-none {
677
+ display: none;
678
+ }
679
+
680
+ .select2-measure-scrollbar {
681
+ position: absolute;
682
+ top: -10000px;
683
+ left: -10000px;
684
+ width: 100px;
685
+ height: 100px;
686
+ overflow: scroll;
687
+ }
688
+
689
+ /* Retina-ize icons */
690
+
691
+ @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 2dppx) {
692
+ .select2-search input,
693
+ .select2-search-choice-close,
694
+ .select2-container .select2-choice abbr,
695
+ .select2-container .select2-choice .select2-arrow b {
696
+ background-image: url('select2x2.png') !important;
697
+ background-repeat: no-repeat !important;
698
+ background-size: 60px 40px !important;
699
+ }
700
+
701
+ .select2-search input {
702
+ background-position: 100% -21px !important;
703
+ }
704
+ }
lib/select2/select2.js ADDED
@@ -0,0 +1,3541 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Copyright 2012 Igor Vaynberg
3
+
4
+ Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014
5
+
6
+ This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
7
+ General Public License version 2 (the "GPL License"). You may choose either license to govern your
8
+ use of this software only upon the condition that you accept all of the terms of either the Apache
9
+ License or the GPL License.
10
+
11
+ You may obtain a copy of the Apache License and the GPL License at:
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+ http://www.gnu.org/licenses/gpl-2.0.html
15
+
16
+ Unless required by applicable law or agreed to in writing, software distributed under the
17
+ Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
18
+ CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
19
+ the specific language governing permissions and limitations under the Apache License and the GPL License.
20
+ */
21
+ (function ($) {
22
+ if(typeof $.fn.each2 == "undefined") {
23
+ $.extend($.fn, {
24
+ /*
25
+ * 4-10 times faster .each replacement
26
+ * use it carefully, as it overrides jQuery context of element on each iteration
27
+ */
28
+ each2 : function (c) {
29
+ var j = $([0]), i = -1, l = this.length;
30
+ while (
31
+ ++i < l
32
+ && (j.context = j[0] = this[i])
33
+ && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
34
+ );
35
+ return this;
36
+ }
37
+ });
38
+ }
39
+ })(jQuery);
40
+
41
+ (function ($, undefined) {
42
+ "use strict";
43
+ /*global document, window, jQuery, console */
44
+
45
+ if (window.Select2 !== undefined) {
46
+ return;
47
+ }
48
+
49
+ var AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
50
+ lastMousePosition={x:0,y:0}, $document, scrollBarDimensions,
51
+
52
+ KEY = {
53
+ TAB: 9,
54
+ ENTER: 13,
55
+ ESC: 27,
56
+ SPACE: 32,
57
+ LEFT: 37,
58
+ UP: 38,
59
+ RIGHT: 39,
60
+ DOWN: 40,
61
+ SHIFT: 16,
62
+ CTRL: 17,
63
+ ALT: 18,
64
+ PAGE_UP: 33,
65
+ PAGE_DOWN: 34,
66
+ HOME: 36,
67
+ END: 35,
68
+ BACKSPACE: 8,
69
+ DELETE: 46,
70
+ isArrow: function (k) {
71
+ k = k.which ? k.which : k;
72
+ switch (k) {
73
+ case KEY.LEFT:
74
+ case KEY.RIGHT:
75
+ case KEY.UP:
76
+ case KEY.DOWN:
77
+ return true;
78
+ }
79
+ return false;
80
+ },
81
+ isControl: function (e) {
82
+ var k = e.which;
83
+ switch (k) {
84
+ case KEY.SHIFT:
85
+ case KEY.CTRL:
86
+ case KEY.ALT:
87
+ return true;
88
+ }
89
+
90
+ if (e.metaKey) return true;
91
+
92
+ return false;
93
+ },
94
+ isFunctionKey: function (k) {
95
+ k = k.which ? k.which : k;
96
+ return k >= 112 && k <= 123;
97
+ }
98
+ },
99
+ MEASURE_SCROLLBAR_TEMPLATE = "<div class='select2-measure-scrollbar'></div>",
100
+
101
+ DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038A":"\u0399","\u03AA":"\u0399","\u038C":"\u039F","\u038E":"\u03A5","\u03AB":"\u03A5","\u038F":"\u03A9","\u03AC":"\u03B1","\u03AD":"\u03B5","\u03AE":"\u03B7","\u03AF":"\u03B9","\u03CA":"\u03B9","\u0390":"\u03B9","\u03CC":"\u03BF","\u03CD":"\u03C5","\u03CB":"\u03C5","\u03B0":"\u03C5","\u03C9":"\u03C9","\u03C2":"\u03C3"};
102
+
103
+ $document = $(document);
104
+
105
+ nextUid=(function() { var counter=1; return function() { return counter++; }; }());
106
+
107
+
108
+ function reinsertElement(element) {
109
+ var placeholder = $(document.createTextNode(''));
110
+
111
+ element.before(placeholder);
112
+ placeholder.before(element);
113
+ placeholder.remove();
114
+ }
115
+
116
+ function stripDiacritics(str) {
117
+ // Used 'uni range + named function' from http://jsperf.com/diacritics/18
118
+ function match(a) {
119
+ return DIACRITICS[a] || a;
120
+ }
121
+
122
+ return str.replace(/[^\u0000-\u007E]/g, match);
123
+ }
124
+
125
+ function indexOf(value, array) {
126
+ var i = 0, l = array.length;
127
+ for (; i < l; i = i + 1) {
128
+ if (equal(value, array[i])) return i;
129
+ }
130
+ return -1;
131
+ }
132
+
133
+ function measureScrollbar () {
134
+ var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
135
+ $template.appendTo(document.body);
136
+
137
+ var dim = {
138
+ width: $template.width() - $template[0].clientWidth,
139
+ height: $template.height() - $template[0].clientHeight
140
+ };
141
+ $template.remove();
142
+
143
+ return dim;
144
+ }
145
+
146
+ /**
147
+ * Compares equality of a and b
148
+ * @param a
149
+ * @param b
150
+ */
151
+ function equal(a, b) {
152
+ if (a === b) return true;
153
+ if (a === undefined || b === undefined) return false;
154
+ if (a === null || b === null) return false;
155
+ // Check whether 'a' or 'b' is a string (primitive or object).
156
+ // The concatenation of an empty string (+'') converts its argument to a string's primitive.
157
+ if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object
158
+ if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object
159
+ return false;
160
+ }
161
+
162
+ /**
163
+ * Splits the string into an array of values, transforming each value. An empty array is returned for nulls or empty
164
+ * strings
165
+ * @param string
166
+ * @param separator
167
+ */
168
+ function splitVal(string, separator, transform) {
169
+ var val, i, l;
170
+ if (string === null || string.length < 1) return [];
171
+ val = string.split(separator);
172
+ for (i = 0, l = val.length; i < l; i = i + 1) val[i] = transform(val[i]);
173
+ return val;
174
+ }
175
+
176
+ function getSideBorderPadding(element) {
177
+ return element.outerWidth(false) - element.width();
178
+ }
179
+
180
+ function installKeyUpChangeEvent(element) {
181
+ var key="keyup-change-value";
182
+ element.on("keydown", function () {
183
+ if ($.data(element, key) === undefined) {
184
+ $.data(element, key, element.val());
185
+ }
186
+ });
187
+ element.on("keyup", function () {
188
+ var val= $.data(element, key);
189
+ if (val !== undefined && element.val() !== val) {
190
+ $.removeData(element, key);
191
+ element.trigger("keyup-change");
192
+ }
193
+ });
194
+ }
195
+
196
+
197
+ /**
198
+ * filters mouse events so an event is fired only if the mouse moved.
199
+ *
200
+ * filters out mouse events that occur when mouse is stationary but
201
+ * the elements under the pointer are scrolled.
202
+ */
203
+ function installFilteredMouseMove(element) {
204
+ element.on("mousemove", function (e) {
205
+ var lastpos = lastMousePosition;
206
+ if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
207
+ $(e.target).trigger("mousemove-filtered", e);
208
+ }
209
+ });
210
+ }
211
+
212
+ /**
213
+ * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
214
+ * within the last quietMillis milliseconds.
215
+ *
216
+ * @param quietMillis number of milliseconds to wait before invoking fn
217
+ * @param fn function to be debounced
218
+ * @param ctx object to be used as this reference within fn
219
+ * @return debounced version of fn
220
+ */
221
+ function debounce(quietMillis, fn, ctx) {
222
+ ctx = ctx || undefined;
223
+ var timeout;
224
+ return function () {
225
+ var args = arguments;
226
+ window.clearTimeout(timeout);
227
+ timeout = window.setTimeout(function() {
228
+ fn.apply(ctx, args);
229
+ }, quietMillis);
230
+ };
231
+ }
232
+
233
+ function installDebouncedScroll(threshold, element) {
234
+ var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
235
+ element.on("scroll", function (e) {
236
+ if (indexOf(e.target, element.get()) >= 0) notify(e);
237
+ });
238
+ }
239
+
240
+ function focus($el) {
241
+ if ($el[0] === document.activeElement) return;
242
+
243
+ /* set the focus in a 0 timeout - that way the focus is set after the processing
244
+ of the current event has finished - which seems like the only reliable way
245
+ to set focus */
246
+ window.setTimeout(function() {
247
+ var el=$el[0], pos=$el.val().length, range;
248
+
249
+ $el.focus();
250
+
251
+ /* make sure el received focus so we do not error out when trying to manipulate the caret.
252
+ sometimes modals or others listeners may steal it after its set */
253
+ var isVisible = (el.offsetWidth > 0 || el.offsetHeight > 0);
254
+ if (isVisible && el === document.activeElement) {
255
+
256
+ /* after the focus is set move the caret to the end, necessary when we val()
257
+ just before setting focus */
258
+ if(el.setSelectionRange)
259
+ {
260
+ el.setSelectionRange(pos, pos);
261
+ }
262
+ else if (el.createTextRange) {
263
+ range = el.createTextRange();
264
+ range.collapse(false);
265
+ range.select();
266
+ }
267
+ }
268
+ }, 0);
269
+ }
270
+
271
+ function getCursorInfo(el) {
272
+ el = $(el)[0];
273
+ var offset = 0;
274
+ var length = 0;
275
+ if ('selectionStart' in el) {
276
+ offset = el.selectionStart;
277
+ length = el.selectionEnd - offset;
278
+ } else if ('selection' in document) {
279
+ el.focus();
280
+ var sel = document.selection.createRange();
281
+ length = document.selection.createRange().text.length;
282
+ sel.moveStart('character', -el.value.length);
283
+ offset = sel.text.length - length;
284
+ }
285
+ return { offset: offset, length: length };
286
+ }
287
+
288
+ function killEvent(event) {
289
+ event.preventDefault();
290
+ event.stopPropagation();
291
+ }
292
+ function killEventImmediately(event) {
293
+ event.preventDefault();
294
+ event.stopImmediatePropagation();
295
+ }
296
+
297
+ function measureTextWidth(e) {
298
+ if (!sizer){
299
+ var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
300
+ sizer = $(document.createElement("div")).css({
301
+ position: "absolute",
302
+ left: "-10000px",
303
+ top: "-10000px",
304
+ display: "none",
305
+ fontSize: style.fontSize,
306
+ fontFamily: style.fontFamily,
307
+ fontStyle: style.fontStyle,
308
+ fontWeight: style.fontWeight,
309
+ letterSpacing: style.letterSpacing,
310
+ textTransform: style.textTransform,
311
+ whiteSpace: "nowrap"
312
+ });
313
+ sizer.attr("class","select2-sizer");
314
+ $(document.body).append(sizer);
315
+ }
316
+ sizer.text(e.val());
317
+ return sizer.width();
318
+ }
319
+
320
+ function syncCssClasses(dest, src, adapter) {
321
+ var classes, replacements = [], adapted;
322
+
323
+ classes = $.trim(dest.attr("class"));
324
+
325
+ if (classes) {
326
+ classes = '' + classes; // for IE which returns object
327
+
328
+ $(classes.split(/\s+/)).each2(function() {
329
+ if (this.indexOf("select2-") === 0) {
330
+ replacements.push(this);
331
+ }
332
+ });
333
+ }
334
+
335
+ classes = $.trim(src.attr("class"));
336
+
337
+ if (classes) {
338
+ classes = '' + classes; // for IE which returns object
339
+
340
+ $(classes.split(/\s+/)).each2(function() {
341
+ if (this.indexOf("select2-") !== 0) {
342
+ adapted = adapter(this);
343
+
344
+ if (adapted) {
345
+ replacements.push(adapted);
346
+ }
347
+ }
348
+ });
349
+ }
350
+
351
+ dest.attr("class", replacements.join(" "));
352
+ }
353
+
354
+
355
+ function markMatch(text, term, markup, escapeMarkup) {
356
+ var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())),
357
+ tl=term.length;
358
+
359
+ if (match<0) {
360
+ markup.push(escapeMarkup(text));
361
+ return;
362
+ }
363
+
364
+ markup.push(escapeMarkup(text.substring(0, match)));
365
+ markup.push("<span class='select2-match'>");
366
+ markup.push(escapeMarkup(text.substring(match, match + tl)));
367
+ markup.push("</span>");
368
+ markup.push(escapeMarkup(text.substring(match + tl, text.length)));
369
+ }
370
+
371
+ function defaultEscapeMarkup(markup) {
372
+ var replace_map = {
373
+ '\\': '&#92;',
374
+ '&': '&amp;',
375
+ '<': '&lt;',
376
+ '>': '&gt;',
377
+ '"': '&quot;',
378
+ "'": '&#39;',
379
+ "/": '&#47;'
380
+ };
381
+
382
+ return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
383
+ return replace_map[match];
384
+ });
385
+ }
386
+
387
+ /**
388
+ * Produces an ajax-based query function
389
+ *
390
+ * @param options object containing configuration parameters
391
+ * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
392
+ * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
393
+ * @param options.url url for the data
394
+ * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
395
+ * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified
396
+ * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
397
+ * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2.
398
+ * The expected format is an object containing the following keys:
399
+ * results array of objects that will be used as choices
400
+ * more (optional) boolean indicating whether there are more results available
401
+ * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
402
+ */
403
+ function ajax(options) {
404
+ var timeout, // current scheduled but not yet executed request
405
+ handler = null,
406
+ quietMillis = options.quietMillis || 100,
407
+ ajaxUrl = options.url,
408
+ self = this;
409
+
410
+ return function (query) {
411
+ window.clearTimeout(timeout);
412
+ timeout = window.setTimeout(function () {
413
+ var data = options.data, // ajax data function
414
+ url = ajaxUrl, // ajax url string or function
415
+ transport = options.transport || $.fn.select2.ajaxDefaults.transport,
416
+ // deprecated - to be removed in 4.0 - use params instead
417
+ deprecated = {
418
+ type: options.type || 'GET', // set type of request (GET or POST)
419
+ cache: options.cache || false,
420
+ jsonpCallback: options.jsonpCallback||undefined,
421
+ dataType: options.dataType||"json"
422
+ },
423
+ params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated);
424
+
425
+ data = data ? data.call(self, query.term, query.page, query.context) : null;
426
+ url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
427
+
428
+ if (handler && typeof handler.abort === "function") { handler.abort(); }
429
+
430
+ if (options.params) {
431
+ if ($.isFunction(options.params)) {
432
+ $.extend(params, options.params.call(self));
433
+ } else {
434
+ $.extend(params, options.params);
435
+ }
436
+ }
437
+
438
+ $.extend(params, {
439
+ url: url,
440
+ dataType: options.dataType,
441
+ data: data,
442
+ success: function (data) {
443
+ // TODO - replace query.page with query so users have access to term, page, etc.
444
+ // added query as third paramter to keep backwards compatibility
445
+ var results = options.results(data, query.page, query);
446
+ query.callback(results);
447
+ },
448
+ error: function(jqXHR, textStatus, errorThrown){
449
+ var results = {
450
+ hasError: true,
451
+ jqXHR: jqXHR,
452
+ textStatus: textStatus,
453
+ errorThrown: errorThrown
454
+ };
455
+
456
+ query.callback(results);
457
+ }
458
+ });
459
+ handler = transport.call(self, params);
460
+ }, quietMillis);
461
+ };
462
+ }
463
+
464
+ /**
465
+ * Produces a query function that works with a local array
466
+ *
467
+ * @param options object containing configuration parameters. The options parameter can either be an array or an
468
+ * object.
469
+ *
470
+ * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
471
+ *
472
+ * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
473
+ * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
474
+ * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
475
+ * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
476
+ * the text.
477
+ */
478
+ function local(options) {
479
+ var data = options, // data elements
480
+ dataText,
481
+ tmp,
482
+ text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
483
+
484
+ if ($.isArray(data)) {
485
+ tmp = data;
486
+ data = { results: tmp };
487
+ }
488
+
489
+ if ($.isFunction(data) === false) {
490
+ tmp = data;
491
+ data = function() { return tmp; };
492
+ }
493
+
494
+ var dataItem = data();
495
+ if (dataItem.text) {
496
+ text = dataItem.text;
497
+ // if text is not a function we assume it to be a key name
498
+ if (!$.isFunction(text)) {
499
+ dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
500
+ text = function (item) { return item[dataText]; };
501
+ }
502
+ }
503
+
504
+ return function (query) {
505
+ var t = query.term, filtered = { results: [] }, process;
506
+ if (t === "") {
507
+ query.callback(data());
508
+ return;
509
+ }
510
+
511
+ process = function(datum, collection) {
512
+ var group, attr;
513
+ datum = datum[0];
514
+ if (datum.children) {
515
+ group = {};
516
+ for (attr in datum) {
517
+ if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
518
+ }
519
+ group.children=[];
520
+ $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
521
+ if (group.children.length || query.matcher(t, text(group), datum)) {
522
+ collection.push(group);
523
+ }
524
+ } else {
525
+ if (query.matcher(t, text(datum), datum)) {
526
+ collection.push(datum);
527
+ }
528
+ }
529
+ };
530
+
531
+ $(data().results).each2(function(i, datum) { process(datum, filtered.results); });
532
+ query.callback(filtered);
533
+ };
534
+ }
535
+
536
+ // TODO javadoc
537
+ function tags(data) {
538
+ var isFunc = $.isFunction(data);
539
+ return function (query) {
540
+ var t = query.term, filtered = {results: []};
541
+ var result = isFunc ? data(query) : data;
542
+ if ($.isArray(result)) {
543
+ $(result).each(function () {
544
+ var isObject = this.text !== undefined,
545
+ text = isObject ? this.text : this;
546
+ if (t === "" || query.matcher(t, text)) {
547
+ filtered.results.push(isObject ? this : {id: this, text: this});
548
+ }
549
+ });
550
+ query.callback(filtered);
551
+ }
552
+ };
553
+ }
554
+
555
+ /**
556
+ * Checks if the formatter function should be used.
557
+ *
558
+ * Throws an error if it is not a function. Returns true if it should be used,
559
+ * false if no formatting should be performed.
560
+ *
561
+ * @param formatter
562
+ */
563
+ function checkFormatter(formatter, formatterName) {
564
+ if ($.isFunction(formatter)) return true;
565
+ if (!formatter) return false;
566
+ if (typeof(formatter) === 'string') return true;
567
+ throw new Error(formatterName +" must be a string, function, or falsy value");
568
+ }
569
+
570
+ /**
571
+ * Returns a given value
572
+ * If given a function, returns its output
573
+ *
574
+ * @param val string|function
575
+ * @param context value of "this" to be passed to function
576
+ * @returns {*}
577
+ */
578
+ function evaluate(val, context) {
579
+ if ($.isFunction(val)) {
580
+ var args = Array.prototype.slice.call(arguments, 2);
581
+ return val.apply(context, args);
582
+ }
583
+ return val;
584
+ }
585
+
586
+ function countResults(results) {
587
+ var count = 0;
588
+ $.each(results, function(i, item) {
589
+ if (item.children) {
590
+ count += countResults(item.children);
591
+ } else {
592
+ count++;
593
+ }
594
+ });
595
+ return count;
596
+ }
597
+
598
+ /**
599
+ * Default tokenizer. This function uses breaks the input on substring match of any string from the
600
+ * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
601
+ * two options have to be defined in order for the tokenizer to work.
602
+ *
603
+ * @param input text user has typed so far or pasted into the search field
604
+ * @param selection currently selected choices
605
+ * @param selectCallback function(choice) callback tho add the choice to selection
606
+ * @param opts select2's opts
607
+ * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
608
+ */
609
+ function defaultTokenizer(input, selection, selectCallback, opts) {
610
+ var original = input, // store the original so we can compare and know if we need to tell the search to update its text
611
+ dupe = false, // check for whether a token we extracted represents a duplicate selected choice
612
+ token, // token
613
+ index, // position at which the separator was found
614
+ i, l, // looping variables
615
+ separator; // the matched separator
616
+
617
+ if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
618
+
619
+ while (true) {
620
+ index = -1;
621
+
622
+ for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
623
+ separator = opts.tokenSeparators[i];
624
+ index = input.indexOf(separator);
625
+ if (index >= 0) break;
626
+ }
627
+
628
+ if (index < 0) break; // did not find any token separator in the input string, bail
629
+
630
+ token = input.substring(0, index);
631
+ input = input.substring(index + separator.length);
632
+
633
+ if (token.length > 0) {
634
+ token = opts.createSearchChoice.call(this, token, selection);
635
+ if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
636
+ dupe = false;
637
+ for (i = 0, l = selection.length; i < l; i++) {
638
+ if (equal(opts.id(token), opts.id(selection[i]))) {
639
+ dupe = true; break;
640
+ }
641
+ }
642
+
643
+ if (!dupe) selectCallback(token);
644
+ }
645
+ }
646
+ }
647
+
648
+ if (original!==input) return input;
649
+ }
650
+
651
+ function cleanupJQueryElements() {
652
+ var self = this;
653
+
654
+ $.each(arguments, function (i, element) {
655
+ self[element].remove();
656
+ self[element] = null;
657
+ });
658
+ }
659
+
660
+ /**
661
+ * Creates a new class
662
+ *
663
+ * @param superClass
664
+ * @param methods
665
+ */
666
+ function clazz(SuperClass, methods) {
667
+ var constructor = function () {};
668
+ constructor.prototype = new SuperClass;
669
+ constructor.prototype.constructor = constructor;
670
+ constructor.prototype.parent = SuperClass.prototype;
671
+ constructor.prototype = $.extend(constructor.prototype, methods);
672
+ return constructor;
673
+ }
674
+
675
+ AbstractSelect2 = clazz(Object, {
676
+
677
+ // abstract
678
+ bind: function (func) {
679
+ var self = this;
680
+ return function () {
681
+ func.apply(self, arguments);
682
+ };
683
+ },
684
+
685
+ // abstract
686
+ init: function (opts) {
687
+ var results, search, resultsSelector = ".select2-results";
688
+
689
+ // prepare options
690
+ this.opts = opts = this.prepareOpts(opts);
691
+
692
+ this.id=opts.id;
693
+
694
+ // destroy if called on an existing component
695
+ if (opts.element.data("select2") !== undefined &&
696
+ opts.element.data("select2") !== null) {
697
+ opts.element.data("select2").destroy();
698
+ }
699
+
700
+ this.container = this.createContainer();
701
+
702
+ this.liveRegion = $('.select2-hidden-accessible');
703
+ if (this.liveRegion.length == 0) {
704
+ this.liveRegion = $("<span>", {
705
+ role: "status",
706
+ "aria-live": "polite"
707
+ })
708
+ .addClass("select2-hidden-accessible")
709
+ .appendTo(document.body);
710
+ }
711
+
712
+ this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
713
+ this.containerEventName= this.containerId
714
+ .replace(/([.])/g, '_')
715
+ .replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
716
+ this.container.attr("id", this.containerId);
717
+
718
+ this.container.attr("title", opts.element.attr("title"));
719
+
720
+ this.body = $(document.body);
721
+
722
+ syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
723
+
724
+ this.container.attr("style", opts.element.attr("style"));
725
+ this.container.css(evaluate(opts.containerCss, this.opts.element));
726
+ this.container.addClass(evaluate(opts.containerCssClass, this.opts.element));
727
+
728
+ this.elementTabIndex = this.opts.element.attr("tabindex");
729
+
730
+ // swap container for the element
731
+ this.opts.element
732
+ .data("select2", this)
733
+ .attr("tabindex", "-1")
734
+ .before(this.container)
735
+ .on("click.select2", killEvent); // do not leak click events
736
+
737
+ this.container.data("select2", this);
738
+
739
+ this.dropdown = this.container.find(".select2-drop");
740
+
741
+ syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
742
+
743
+ this.dropdown.addClass(evaluate(opts.dropdownCssClass, this.opts.element));
744
+ this.dropdown.data("select2", this);
745
+ this.dropdown.on("click", killEvent);
746
+
747
+ this.results = results = this.container.find(resultsSelector);
748
+ this.search = search = this.container.find("input.select2-input");
749
+
750
+ this.queryCount = 0;
751
+ this.resultsPage = 0;
752
+ this.context = null;
753
+
754
+ // initialize the container
755
+ this.initContainer();
756
+
757
+ this.container.on("click", killEvent);
758
+
759
+ installFilteredMouseMove(this.results);
760
+
761
+ this.dropdown.on("mousemove-filtered", resultsSelector, this.bind(this.highlightUnderEvent));
762
+ this.dropdown.on("touchstart touchmove touchend", resultsSelector, this.bind(function (event) {
763
+ this._touchEvent = true;
764
+ this.highlightUnderEvent(event);
765
+ }));
766
+ this.dropdown.on("touchmove", resultsSelector, this.bind(this.touchMoved));
767
+ this.dropdown.on("touchstart touchend", resultsSelector, this.bind(this.clearTouchMoved));
768
+
769
+ // Waiting for a click event on touch devices to select option and hide dropdown
770
+ // otherwise click will be triggered on an underlying element
771
+ this.dropdown.on('click', this.bind(function (event) {
772
+ if (this._touchEvent) {
773
+ this._touchEvent = false;
774
+ this.selectHighlighted();
775
+ }
776
+ }));
777
+
778
+ installDebouncedScroll(80, this.results);
779
+ this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
780
+
781
+ // do not propagate change event from the search field out of the component
782
+ $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
783
+ $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
784
+
785
+ // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
786
+ if ($.fn.mousewheel) {
787
+ results.mousewheel(function (e, delta, deltaX, deltaY) {
788
+ var top = results.scrollTop();
789
+ if (deltaY > 0 && top - deltaY <= 0) {
790
+ results.scrollTop(0);
791
+ killEvent(e);
792
+ } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
793
+ results.scrollTop(results.get(0).scrollHeight - results.height());
794
+ killEvent(e);
795
+ }
796
+ });
797
+ }
798
+
799
+ installKeyUpChangeEvent(search);
800
+ search.on("keyup-change input paste", this.bind(this.updateResults));
801
+ search.on("focus", function () { search.addClass("select2-focused"); });
802
+ search.on("blur", function () { search.removeClass("select2-focused");});
803
+
804
+ this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
805
+ if ($(e.target).closest(".select2-result-selectable").length > 0) {
806
+ this.highlightUnderEvent(e);
807
+ this.selectHighlighted(e);
808
+ }
809
+ }));
810
+
811
+ // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
812
+ // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
813
+ // dom it will trigger the popup close, which is not what we want
814
+ // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal.
815
+ this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); });
816
+
817
+ this.nextSearchTerm = undefined;
818
+
819
+ if ($.isFunction(this.opts.initSelection)) {
820
+ // initialize selection based on the current value of the source element
821
+ this.initSelection();
822
+
823
+ // if the user has provided a function that can set selection based on the value of the source element
824
+ // we monitor the change event on the element and trigger it, allowing for two way synchronization
825
+ this.monitorSource();
826
+ }
827
+
828
+ if (opts.maximumInputLength !== null) {
829
+ this.search.attr("maxlength", opts.maximumInputLength);
830
+ }
831
+
832
+ var disabled = opts.element.prop("disabled");
833
+ if (disabled === undefined) disabled = false;
834
+ this.enable(!disabled);
835
+
836
+ var readonly = opts.element.prop("readonly");
837
+ if (readonly === undefined) readonly = false;
838
+ this.readonly(readonly);
839
+
840
+ // Calculate size of scrollbar
841
+ scrollBarDimensions = scrollBarDimensions || measureScrollbar();
842
+
843
+ this.autofocus = opts.element.prop("autofocus");
844
+ opts.element.prop("autofocus", false);
845
+ if (this.autofocus) this.focus();
846
+
847
+ this.search.attr("placeholder", opts.searchInputPlaceholder);
848
+ },
849
+
850
+ // abstract
851
+ destroy: function () {
852
+ var element=this.opts.element, select2 = element.data("select2"), self = this;
853
+
854
+ this.close();
855
+
856
+ if (element.length && element[0].detachEvent && self._sync) {
857
+ element.each(function () {
858
+ if (self._sync) {
859
+ this.detachEvent("onpropertychange", self._sync);
860
+ }
861
+ });
862
+ }
863
+ if (this.propertyObserver) {
864
+ this.propertyObserver.disconnect();
865
+ this.propertyObserver = null;
866
+ }
867
+ this._sync = null;
868
+
869
+ if (select2 !== undefined) {
870
+ select2.container.remove();
871
+ select2.liveRegion.remove();
872
+ select2.dropdown.remove();
873
+ element
874
+ .show()
875
+ .removeData("select2")
876
+ .off(".select2")
877
+ .prop("autofocus", this.autofocus || false);
878
+ if (this.elementTabIndex) {
879
+ element.attr({tabindex: this.elementTabIndex});
880
+ } else {
881
+ element.removeAttr("tabindex");
882
+ }
883
+ element.show();
884
+ }
885
+
886
+ cleanupJQueryElements.call(this,
887
+ "container",
888
+ "liveRegion",
889
+ "dropdown",
890
+ "results",
891
+ "search"
892
+ );
893
+ },
894
+
895
+ // abstract
896
+ optionToData: function(element) {
897
+ if (element.is("option")) {
898
+ return {
899
+ id:element.prop("value"),
900
+ text:element.text(),
901
+ element: element.get(),
902
+ css: element.attr("class"),
903
+ disabled: element.prop("disabled"),
904
+ locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true)
905
+ };
906
+ } else if (element.is("optgroup")) {
907
+ return {
908
+ text:element.attr("label"),
909
+ children:[],
910
+ element: element.get(),
911
+ css: element.attr("class")
912
+ };
913
+ }
914
+ },
915
+
916
+ // abstract
917
+ prepareOpts: function (opts) {
918
+ var element, select, idKey, ajaxUrl, self = this;
919
+
920
+ element = opts.element;
921
+
922
+ if (element.get(0).tagName.toLowerCase() === "select") {
923
+ this.select = select = opts.element;
924
+ }
925
+
926
+ if (select) {
927
+ // these options are not allowed when attached to a select because they are picked up off the element itself
928
+ $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
929
+ if (this in opts) {
930
+ throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
931
+ }
932
+ });
933
+ }
934
+
935
+ opts = $.extend({}, {
936
+ populateResults: function(container, results, query) {
937
+ var populate, id=this.opts.id, liveRegion=this.liveRegion;
938
+
939
+ populate=function(results, container, depth) {
940
+
941
+ var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
942
+
943
+ results = opts.sortResults(results, container, query);
944
+
945
+ // collect the created nodes for bulk append
946
+ var nodes = [];
947
+ for (i = 0, l = results.length; i < l; i = i + 1) {
948
+
949
+ result=results[i];
950
+
951
+ disabled = (result.disabled === true);
952
+ selectable = (!disabled) && (id(result) !== undefined);
953
+
954
+ compound=result.children && result.children.length > 0;
955
+
956
+ node=$("<li></li>");
957
+ node.addClass("select2-results-dept-"+depth);
958
+ node.addClass("select2-result");
959
+ node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
960
+ if (disabled) { node.addClass("select2-disabled"); }
961
+ if (compound) { node.addClass("select2-result-with-children"); }
962
+ node.addClass(self.opts.formatResultCssClass(result));
963
+ node.attr("role", "presentation");
964
+
965
+ label=$(document.createElement("div"));
966
+ label.addClass("select2-result-label");
967
+ label.attr("id", "select2-result-label-" + nextUid());
968
+ label.attr("role", "option");
969
+
970
+ formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
971
+ if (formatted!==undefined) {
972
+ label.html(formatted);
973
+ node.append(label);
974
+ }
975
+
976
+
977
+ if (compound) {
978
+
979
+ innerContainer=$("<ul></ul>");
980
+ innerContainer.addClass("select2-result-sub");
981
+ populate(result.children, innerContainer, depth+1);
982
+ node.append(innerContainer);
983
+ }
984
+
985
+ node.data("select2-data", result);
986
+ nodes.push(node[0]);
987
+ }
988
+
989
+ // bulk append the created nodes
990
+ container.append(nodes);
991
+ liveRegion.text(opts.formatMatches(results.length));
992
+ };
993
+
994
+ populate(results, container, 0);
995
+ }
996
+ }, $.fn.select2.defaults, opts);
997
+
998
+ if (typeof(opts.id) !== "function") {
999
+ idKey = opts.id;
1000
+ opts.id = function (e) { return e[idKey]; };
1001
+ }
1002
+
1003
+ if ($.isArray(opts.element.data("select2Tags"))) {
1004
+ if ("tags" in opts) {
1005
+ throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
1006
+ }
1007
+ opts.tags=opts.element.data("select2Tags");
1008
+ }
1009
+
1010
+ if (select) {
1011
+ opts.query = this.bind(function (query) {
1012
+ var data = { results: [], more: false },
1013
+ term = query.term,
1014
+ children, placeholderOption, process;
1015
+
1016
+ process=function(element, collection) {
1017
+ var group;
1018
+ if (element.is("option")) {
1019
+ if (query.matcher(term, element.text(), element)) {
1020
+ collection.push(self.optionToData(element));
1021
+ }
1022
+ } else if (element.is("optgroup")) {
1023
+ group=self.optionToData(element);
1024
+ element.children().each2(function(i, elm) { process(elm, group.children); });
1025
+ if (group.children.length>0) {
1026
+ collection.push(group);
1027
+ }
1028
+ }
1029
+ };
1030
+
1031
+ children=element.children();
1032
+
1033
+ // ignore the placeholder option if there is one
1034
+ if (this.getPlaceholder() !== undefined && children.length > 0) {
1035
+ placeholderOption = this.getPlaceholderOption();
1036
+ if (placeholderOption) {
1037
+ children=children.not(placeholderOption);
1038
+ }
1039
+ }
1040
+
1041
+ children.each2(function(i, elm) { process(elm, data.results); });
1042
+
1043
+ query.callback(data);
1044
+ });
1045
+ // this is needed because inside val() we construct choices from options and their id is hardcoded
1046
+ opts.id=function(e) { return e.id; };
1047
+ } else {
1048
+ if (!("query" in opts)) {
1049
+
1050
+ if ("ajax" in opts) {
1051
+ ajaxUrl = opts.element.data("ajax-url");
1052
+ if (ajaxUrl && ajaxUrl.length > 0) {
1053
+ opts.ajax.url = ajaxUrl;
1054
+ }
1055
+ opts.query = ajax.call(opts.element, opts.ajax);
1056
+ } else if ("data" in opts) {
1057
+ opts.query = local(opts.data);
1058
+ } else if ("tags" in opts) {
1059
+ opts.query = tags(opts.tags);
1060
+ if (opts.createSearchChoice === undefined) {
1061
+ opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
1062
+ }
1063
+ if (opts.initSelection === undefined) {
1064
+ opts.initSelection = function (element, callback) {
1065
+ var data = [];
1066
+ $(splitVal(element.val(), opts.separator, opts.transformVal)).each(function () {
1067
+ var obj = { id: this, text: this },
1068
+ tags = opts.tags;
1069
+ if ($.isFunction(tags)) tags=tags();
1070
+ $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
1071
+ data.push(obj);
1072
+ });
1073
+
1074
+ callback(data);
1075
+ };
1076
+ }
1077
+ }
1078
+ }
1079
+ }
1080
+ if (typeof(opts.query) !== "function") {
1081
+ throw "query function not defined for Select2 " + opts.element.attr("id");
1082
+ }
1083
+
1084
+ if (opts.createSearchChoicePosition === 'top') {
1085
+ opts.createSearchChoicePosition = function(list, item) { list.unshift(item); };
1086
+ }
1087
+ else if (opts.createSearchChoicePosition === 'bottom') {
1088
+ opts.createSearchChoicePosition = function(list, item) { list.push(item); };
1089
+ }
1090
+ else if (typeof(opts.createSearchChoicePosition) !== "function") {
1091
+ throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
1092
+ }
1093
+
1094
+ return opts;
1095
+ },
1096
+
1097
+ /**
1098
+ * Monitor the original element for changes and update select2 accordingly
1099
+ */
1100
+ // abstract
1101
+ monitorSource: function () {
1102
+ var el = this.opts.element, observer, self = this;
1103
+
1104
+ el.on("change.select2", this.bind(function (e) {
1105
+ if (this.opts.element.data("select2-change-triggered") !== true) {
1106
+ this.initSelection();
1107
+ }
1108
+ }));
1109
+
1110
+ this._sync = this.bind(function () {
1111
+
1112
+ // sync enabled state
1113
+ var disabled = el.prop("disabled");
1114
+ if (disabled === undefined) disabled = false;
1115
+ this.enable(!disabled);
1116
+
1117
+ var readonly = el.prop("readonly");
1118
+ if (readonly === undefined) readonly = false;
1119
+ this.readonly(readonly);
1120
+
1121
+ if (this.container) {
1122
+ syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
1123
+ this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element));
1124
+ }
1125
+
1126
+ if (this.dropdown) {
1127
+ syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
1128
+ this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element));
1129
+ }
1130
+
1131
+ });
1132
+
1133
+ // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener)
1134
+ if (el.length && el[0].attachEvent) {
1135
+ el.each(function() {
1136
+ this.attachEvent("onpropertychange", self._sync);
1137
+ });
1138
+ }
1139
+
1140
+ // safari, chrome, firefox, IE11
1141
+ observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver;
1142
+ if (observer !== undefined) {
1143
+ if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
1144
+ this.propertyObserver = new observer(function (mutations) {
1145
+ $.each(mutations, self._sync);
1146
+ });
1147
+ this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
1148
+ }
1149
+ },
1150
+
1151
+ // abstract
1152
+ triggerSelect: function(data) {
1153
+ var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data });
1154
+ this.opts.element.trigger(evt);
1155
+ return !evt.isDefaultPrevented();
1156
+ },
1157
+
1158
+ /**
1159
+ * Triggers the change event on the source element
1160
+ */
1161
+ // abstract
1162
+ triggerChange: function (details) {
1163
+
1164
+ details = details || {};
1165
+ details= $.extend({}, details, { type: "change", val: this.val() });
1166
+ // prevents recursive triggering
1167
+ this.opts.element.data("select2-change-triggered", true);
1168
+ this.opts.element.trigger(details);
1169
+ this.opts.element.data("select2-change-triggered", false);
1170
+
1171
+ // some validation frameworks ignore the change event and listen instead to keyup, click for selects
1172
+ // so here we trigger the click event manually
1173
+ this.opts.element.click();
1174
+
1175
+ // ValidationEngine ignores the change event and listens instead to blur
1176
+ // so here we trigger the blur event manually if so desired
1177
+ if (this.opts.blurOnChange)
1178
+ this.opts.element.blur();
1179
+ },
1180
+
1181
+ //abstract
1182
+ isInterfaceEnabled: function()
1183
+ {
1184
+ return this.enabledInterface === true;
1185
+ },
1186
+
1187
+ // abstract
1188
+ enableInterface: function() {
1189
+ var enabled = this._enabled && !this._readonly,
1190
+ disabled = !enabled;
1191
+
1192
+ if (enabled === this.enabledInterface) return false;
1193
+
1194
+ this.container.toggleClass("select2-container-disabled", disabled);
1195
+ this.close();
1196
+ this.enabledInterface = enabled;
1197
+
1198
+ return true;
1199
+ },
1200
+
1201
+ // abstract
1202
+ enable: function(enabled) {
1203
+ if (enabled === undefined) enabled = true;
1204
+ if (this._enabled === enabled) return;
1205
+ this._enabled = enabled;
1206
+
1207
+ this.opts.element.prop("disabled", !enabled);
1208
+ this.enableInterface();
1209
+ },
1210
+
1211
+ // abstract
1212
+ disable: function() {
1213
+ this.enable(false);
1214
+ },
1215
+
1216
+ // abstract
1217
+ readonly: function(enabled) {
1218
+ if (enabled === undefined) enabled = false;
1219
+ if (this._readonly === enabled) return;
1220
+ this._readonly = enabled;
1221
+
1222
+ this.opts.element.prop("readonly", enabled);
1223
+ this.enableInterface();
1224
+ },
1225
+
1226
+ // abstract
1227
+ opened: function () {
1228
+ return (this.container) ? this.container.hasClass("select2-dropdown-open") : false;
1229
+ },
1230
+
1231
+ // abstract
1232
+ positionDropdown: function() {
1233
+ var $dropdown = this.dropdown,
1234
+ container = this.container,
1235
+ offset = container.offset(),
1236
+ height = container.outerHeight(false),
1237
+ width = container.outerWidth(false),
1238
+ dropHeight = $dropdown.outerHeight(false),
1239
+ $window = $(window),
1240
+ windowWidth = $window.width(),
1241
+ windowHeight = $window.height(),
1242
+ viewPortRight = $window.scrollLeft() + windowWidth,
1243
+ viewportBottom = $window.scrollTop() + windowHeight,
1244
+ dropTop = offset.top + height,
1245
+ dropLeft = offset.left,
1246
+ enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
1247
+ enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(),
1248
+ dropWidth = $dropdown.outerWidth(false),
1249
+ enoughRoomOnRight = function() {
1250
+ return dropLeft + dropWidth <= viewPortRight;
1251
+ },
1252
+ enoughRoomOnLeft = function() {
1253
+ return offset.left + viewPortRight + container.outerWidth(false) > dropWidth;
1254
+ },
1255
+ aboveNow = $dropdown.hasClass("select2-drop-above"),
1256
+ bodyOffset,
1257
+ above,
1258
+ changeDirection,
1259
+ css,
1260
+ resultsListNode;
1261
+
1262
+ // always prefer the current above/below alignment, unless there is not enough room
1263
+ if (aboveNow) {
1264
+ above = true;
1265
+ if (!enoughRoomAbove && enoughRoomBelow) {
1266
+ changeDirection = true;
1267
+ above = false;
1268
+ }
1269
+ } else {
1270
+ above = false;
1271
+ if (!enoughRoomBelow && enoughRoomAbove) {
1272
+ changeDirection = true;
1273
+ above = true;
1274
+ }
1275
+ }
1276
+
1277
+ //if we are changing direction we need to get positions when dropdown is hidden;
1278
+ if (changeDirection) {
1279
+ $dropdown.hide();
1280
+ offset = this.container.offset();
1281
+ height = this.container.outerHeight(false);
1282
+ width = this.container.outerWidth(false);
1283
+ dropHeight = $dropdown.outerHeight(false);
1284
+ viewPortRight = $window.scrollLeft() + windowWidth;
1285
+ viewportBottom = $window.scrollTop() + windowHeight;
1286
+ dropTop = offset.top + height;
1287
+ dropLeft = offset.left;
1288
+ dropWidth = $dropdown.outerWidth(false);
1289
+ $dropdown.show();
1290
+
1291
+ // fix so the cursor does not move to the left within the search-textbox in IE
1292
+ this.focusSearch();
1293
+ }
1294
+
1295
+ if (this.opts.dropdownAutoWidth) {
1296
+ resultsListNode = $('.select2-results', $dropdown)[0];
1297
+ $dropdown.addClass('select2-drop-auto-width');
1298
+ $dropdown.css('width', '');
1299
+ // Add scrollbar width to dropdown if vertical scrollbar is present
1300
+ dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
1301
+ dropWidth > width ? width = dropWidth : dropWidth = width;
1302
+ dropHeight = $dropdown.outerHeight(false);
1303
+ }
1304
+ else {
1305
+ this.container.removeClass('select2-drop-auto-width');
1306
+ }
1307
+
1308
+ //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
1309
+ //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove);
1310
+
1311
+ // fix positioning when body has an offset and is not position: static
1312
+ if (this.body.css('position') !== 'static') {
1313
+ bodyOffset = this.body.offset();
1314
+ dropTop -= bodyOffset.top;
1315
+ dropLeft -= bodyOffset.left;
1316
+ }
1317
+
1318
+ if (!enoughRoomOnRight() && enoughRoomOnLeft()) {
1319
+ dropLeft = offset.left + this.container.outerWidth(false) - dropWidth;
1320
+ }
1321
+
1322
+ css = {
1323
+ left: dropLeft,
1324
+ width: width
1325
+ };
1326
+
1327
+ if (above) {
1328
+ css.top = offset.top - dropHeight;
1329
+ css.bottom = 'auto';
1330
+ this.container.addClass("select2-drop-above");
1331
+ $dropdown.addClass("select2-drop-above");
1332
+ }
1333
+ else {
1334
+ css.top = dropTop;
1335
+ css.bottom = 'auto';
1336
+ this.container.removeClass("select2-drop-above");
1337
+ $dropdown.removeClass("select2-drop-above");
1338
+ }
1339
+ css = $.extend(css, evaluate(this.opts.dropdownCss, this.opts.element));
1340
+
1341
+ $dropdown.css(css);
1342
+ },
1343
+
1344
+ // abstract
1345
+ shouldOpen: function() {
1346
+ var event;
1347
+
1348
+ if (this.opened()) return false;
1349
+
1350
+ if (this._enabled === false || this._readonly === true) return false;
1351
+
1352
+ event = $.Event("select2-opening");
1353
+ this.opts.element.trigger(event);
1354
+ return !event.isDefaultPrevented();
1355
+ },
1356
+
1357
+ // abstract
1358
+ clearDropdownAlignmentPreference: function() {
1359
+ // clear the classes used to figure out the preference of where the dropdown should be opened
1360
+ this.container.removeClass("select2-drop-above");
1361
+ this.dropdown.removeClass("select2-drop-above");
1362
+ },
1363
+
1364
+ /**
1365
+ * Opens the dropdown
1366
+ *
1367
+ * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
1368
+ * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
1369
+ */
1370
+ // abstract
1371
+ open: function () {
1372
+
1373
+ if (!this.shouldOpen()) return false;
1374
+
1375
+ this.opening();
1376
+
1377
+ // Only bind the document mousemove when the dropdown is visible
1378
+ $document.on("mousemove.select2Event", function (e) {
1379
+ lastMousePosition.x = e.pageX;
1380
+ lastMousePosition.y = e.pageY;
1381
+ });
1382
+
1383
+ return true;
1384
+ },
1385
+
1386
+ /**
1387
+ * Performs the opening of the dropdown
1388
+ */
1389
+ // abstract
1390
+ opening: function() {
1391
+ var cid = this.containerEventName,
1392
+ scroll = "scroll." + cid,
1393
+ resize = "resize."+cid,
1394
+ orient = "orientationchange."+cid,
1395
+ mask;
1396
+
1397
+ this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
1398
+
1399
+ this.clearDropdownAlignmentPreference();
1400
+
1401
+ if(this.dropdown[0] !== this.body.children().last()[0]) {
1402
+ this.dropdown.detach().appendTo(this.body);
1403
+ }
1404
+
1405
+ // create the dropdown mask if doesn't already exist
1406
+ mask = $("#select2-drop-mask");
1407
+ if (mask.length === 0) {
1408
+ mask = $(document.createElement("div"));
1409
+ mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
1410
+ mask.hide();
1411
+ mask.appendTo(this.body);
1412
+ mask.on("mousedown touchstart click", function (e) {
1413
+ // Prevent IE from generating a click event on the body
1414
+ reinsertElement(mask);
1415
+
1416
+ var dropdown = $("#select2-drop"), self;
1417
+ if (dropdown.length > 0) {
1418
+ self=dropdown.data("select2");
1419
+ if (self.opts.selectOnBlur) {
1420
+ self.selectHighlighted({noFocus: true});
1421
+ }
1422
+ self.close();
1423
+ e.preventDefault();
1424
+ e.stopPropagation();
1425
+ }
1426
+ });
1427
+ }
1428
+
1429
+ // ensure the mask is always right before the dropdown
1430
+ if (this.dropdown.prev()[0] !== mask[0]) {
1431
+ this.dropdown.before(mask);
1432
+ }
1433
+
1434
+ // move the global id to the correct dropdown
1435
+ $("#select2-drop").removeAttr("id");
1436
+ this.dropdown.attr("id", "select2-drop");
1437
+
1438
+ // show the elements
1439
+ mask.show();
1440
+
1441
+ this.positionDropdown();
1442
+ this.dropdown.show();
1443
+ this.positionDropdown();
1444
+
1445
+ this.dropdown.addClass("select2-drop-active");
1446
+
1447
+ // attach listeners to events that can change the position of the container and thus require
1448
+ // the position of the dropdown to be updated as well so it does not come unglued from the container
1449
+ var that = this;
1450
+ this.container.parents().add(window).each(function () {
1451
+ $(this).on(resize+" "+scroll+" "+orient, function (e) {
1452
+ if (that.opened()) that.positionDropdown();
1453
+ });
1454
+ });
1455
+
1456
+
1457
+ },
1458
+
1459
+ // abstract
1460
+ close: function () {
1461
+ if (!this.opened()) return;
1462
+
1463
+ var cid = this.containerEventName,
1464
+ scroll = "scroll." + cid,
1465
+ resize = "resize."+cid,
1466
+ orient = "orientationchange."+cid;
1467
+
1468
+ // unbind event listeners
1469
+ this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
1470
+
1471
+ this.clearDropdownAlignmentPreference();
1472
+
1473
+ $("#select2-drop-mask").hide();
1474
+ this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
1475
+ this.dropdown.hide();
1476
+ this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
1477
+ this.results.empty();
1478
+
1479
+ // Now that the dropdown is closed, unbind the global document mousemove event
1480
+ $document.off("mousemove.select2Event");
1481
+
1482
+ this.clearSearch();
1483
+ this.search.removeClass("select2-active");
1484
+ this.opts.element.trigger($.Event("select2-close"));
1485
+ },
1486
+
1487
+ /**
1488
+ * Opens control, sets input value, and updates results.
1489
+ */
1490
+ // abstract
1491
+ externalSearch: function (term) {
1492
+ this.open();
1493
+ this.search.val(term);
1494
+ this.updateResults(false);
1495
+ },
1496
+
1497
+ // abstract
1498
+ clearSearch: function () {
1499
+
1500
+ },
1501
+
1502
+ //abstract
1503
+ getMaximumSelectionSize: function() {
1504
+ return evaluate(this.opts.maximumSelectionSize, this.opts.element);
1505
+ },
1506
+
1507
+ // abstract
1508
+ ensureHighlightVisible: function () {
1509
+ var results = this.results, children, index, child, hb, rb, y, more, topOffset;
1510
+
1511
+ index = this.highlight();
1512
+
1513
+ if (index < 0) return;
1514
+
1515
+ if (index == 0) {
1516
+
1517
+ // if the first element is highlighted scroll all the way to the top,
1518
+ // that way any unselectable headers above it will also be scrolled
1519
+ // into view
1520
+
1521
+ results.scrollTop(0);
1522
+ return;
1523
+ }
1524
+
1525
+ children = this.findHighlightableChoices().find('.select2-result-label');
1526
+
1527
+ child = $(children[index]);
1528
+
1529
+ topOffset = (child.offset() || {}).top || 0;
1530
+
1531
+ hb = topOffset + child.outerHeight(true);
1532
+
1533
+ // if this is the last child lets also make sure select2-more-results is visible
1534
+ if (index === children.length - 1) {
1535
+ more = results.find("li.select2-more-results");
1536
+ if (more.length > 0) {
1537
+ hb = more.offset().top + more.outerHeight(true);
1538
+ }
1539
+ }
1540
+
1541
+ rb = results.offset().top + results.outerHeight(false);
1542
+ if (hb > rb) {
1543
+ results.scrollTop(results.scrollTop() + (hb - rb));
1544
+ }
1545
+ y = topOffset - results.offset().top;
1546
+
1547
+ // make sure the top of the element is visible
1548
+ if (y < 0 && child.css('display') != 'none' ) {
1549
+ results.scrollTop(results.scrollTop() + y); // y is negative
1550
+ }
1551
+ },
1552
+
1553
+ // abstract
1554
+ findHighlightableChoices: function() {
1555
+ return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)");
1556
+ },
1557
+
1558
+ // abstract
1559
+ moveHighlight: function (delta) {
1560
+ var choices = this.findHighlightableChoices(),
1561
+ index = this.highlight();
1562
+
1563
+ while (index > -1 && index < choices.length) {
1564
+ index += delta;
1565
+ var choice = $(choices[index]);
1566
+ if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
1567
+ this.highlight(index);
1568
+ break;
1569
+ }
1570
+ }
1571
+ },
1572
+
1573
+ // abstract
1574
+ highlight: function (index) {
1575
+ var choices = this.findHighlightableChoices(),
1576
+ choice,
1577
+ data;
1578
+
1579
+ if (arguments.length === 0) {
1580
+ return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
1581
+ }
1582
+
1583
+ if (index >= choices.length) index = choices.length - 1;
1584
+ if (index < 0) index = 0;
1585
+
1586
+ this.removeHighlight();
1587
+
1588
+ choice = $(choices[index]);
1589
+ choice.addClass("select2-highlighted");
1590
+
1591
+ // ensure assistive technology can determine the active choice
1592
+ this.search.attr("aria-activedescendant", choice.find(".select2-result-label").attr("id"));
1593
+
1594
+ this.ensureHighlightVisible();
1595
+
1596
+ this.liveRegion.text(choice.text());
1597
+
1598
+ data = choice.data("select2-data");
1599
+ if (data) {
1600
+ this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
1601
+ }
1602
+ },
1603
+
1604
+ removeHighlight: function() {
1605
+ this.results.find(".select2-highlighted").removeClass("select2-highlighted");
1606
+ },
1607
+
1608
+ touchMoved: function() {
1609
+ this._touchMoved = true;
1610
+ },
1611
+
1612
+ clearTouchMoved: function() {
1613
+ this._touchMoved = false;
1614
+ },
1615
+
1616
+ // abstract
1617
+ countSelectableResults: function() {
1618
+ return this.findHighlightableChoices().length;
1619
+ },
1620
+
1621
+ // abstract
1622
+ highlightUnderEvent: function (event) {
1623
+ var el = $(event.target).closest(".select2-result-selectable");
1624
+ if (el.length > 0 && !el.is(".select2-highlighted")) {
1625
+ var choices = this.findHighlightableChoices();
1626
+ this.highlight(choices.index(el));
1627
+ } else if (el.length == 0) {
1628
+ // if we are over an unselectable item remove all highlights
1629
+ this.removeHighlight();
1630
+ }
1631
+ },
1632
+
1633
+ // abstract
1634
+ loadMoreIfNeeded: function () {
1635
+ var results = this.results,
1636
+ more = results.find("li.select2-more-results"),
1637
+ below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
1638
+ page = this.resultsPage + 1,
1639
+ self=this,
1640
+ term=this.search.val(),
1641
+ context=this.context;
1642
+
1643
+ if (more.length === 0) return;
1644
+ below = more.offset().top - results.offset().top - results.height();
1645
+
1646
+ if (below <= this.opts.loadMorePadding) {
1647
+ more.addClass("select2-active");
1648
+ this.opts.query({
1649
+ element: this.opts.element,
1650
+ term: term,
1651
+ page: page,
1652
+ context: context,
1653
+ matcher: this.opts.matcher,
1654
+ callback: this.bind(function (data) {
1655
+
1656
+ // ignore a response if the select2 has been closed before it was received
1657
+ if (!self.opened()) return;
1658
+
1659
+
1660
+ self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
1661
+ self.postprocessResults(data, false, false);
1662
+
1663
+ if (data.more===true) {
1664
+ more.detach().appendTo(results).html(self.opts.escapeMarkup(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)));
1665
+ window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
1666
+ } else {
1667
+ more.remove();
1668
+ }
1669
+ self.positionDropdown();
1670
+ self.resultsPage = page;
1671
+ self.context = data.context;
1672
+ this.opts.element.trigger({ type: "select2-loaded", items: data });
1673
+ })});
1674
+ }
1675
+ },
1676
+
1677
+ /**
1678
+ * Default tokenizer function which does nothing
1679
+ */
1680
+ tokenize: function() {
1681
+
1682
+ },
1683
+
1684
+ /**
1685
+ * @param initial whether or not this is the call to this method right after the dropdown has been opened
1686
+ */
1687
+ // abstract
1688
+ updateResults: function (initial) {
1689
+ var search = this.search,
1690
+ results = this.results,
1691
+ opts = this.opts,
1692
+ data,
1693
+ self = this,
1694
+ input,
1695
+ term = search.val(),
1696
+ lastTerm = $.data(this.container, "select2-last-term"),
1697
+ // sequence number used to drop out-of-order responses
1698
+ queryNumber;
1699
+
1700
+ // prevent duplicate queries against the same term
1701
+ if (initial !== true && lastTerm && equal(term, lastTerm)) return;
1702
+
1703
+ $.data(this.container, "select2-last-term", term);
1704
+
1705
+ // if the search is currently hidden we do not alter the results
1706
+ if (initial !== true && (this.showSearchInput === false || !this.opened())) {
1707
+ return;
1708
+ }
1709
+
1710
+ function postRender() {
1711
+ search.removeClass("select2-active");
1712
+ self.positionDropdown();
1713
+ if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) {
1714
+ self.liveRegion.text(results.text());
1715
+ }
1716
+ else {
1717
+ self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable:not(".select2-selected")').length));
1718
+ }
1719
+ }
1720
+
1721
+ function render(html) {
1722
+ results.html(html);
1723
+ postRender();
1724
+ }
1725
+
1726
+ queryNumber = ++this.queryCount;
1727
+
1728
+ var maxSelSize = this.getMaximumSelectionSize();
1729
+ if (maxSelSize >=1) {
1730
+ data = this.data();
1731
+ if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
1732
+ render("<li class='select2-selection-limit'>" + evaluate(opts.formatSelectionTooBig, opts.element, maxSelSize) + "</li>");
1733
+ return;
1734
+ }
1735
+ }
1736
+
1737
+ if (search.val().length < opts.minimumInputLength) {
1738
+ if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
1739
+ render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooShort, opts.element, search.val(), opts.minimumInputLength) + "</li>");
1740
+ } else {
1741
+ render("");
1742
+ }
1743
+ if (initial && this.showSearch) this.showSearch(true);
1744
+ return;
1745
+ }
1746
+
1747
+ if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
1748
+ if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
1749
+ render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooLong, opts.element, search.val(), opts.maximumInputLength) + "</li>");
1750
+ } else {
1751
+ render("");
1752
+ }
1753
+ return;
1754
+ }
1755
+
1756
+ if (opts.formatSearching && this.findHighlightableChoices().length === 0) {
1757
+ render("<li class='select2-searching'>" + evaluate(opts.formatSearching, opts.element) + "</li>");
1758
+ }
1759
+
1760
+ search.addClass("select2-active");
1761
+
1762
+ this.removeHighlight();
1763
+
1764
+ // give the tokenizer a chance to pre-process the input
1765
+ input = this.tokenize();
1766
+ if (input != undefined && input != null) {
1767
+ search.val(input);
1768
+ }
1769
+
1770
+ this.resultsPage = 1;
1771
+
1772
+ opts.query({
1773
+ element: opts.element,
1774
+ term: search.val(),
1775
+ page: this.resultsPage,
1776
+ context: null,
1777
+ matcher: opts.matcher,
1778
+ callback: this.bind(function (data) {
1779
+ var def; // default choice
1780
+
1781
+ // ignore old responses
1782
+ if (queryNumber != this.queryCount) {
1783
+ return;
1784
+ }
1785
+
1786
+ // ignore a response if the select2 has been closed before it was received
1787
+ if (!this.opened()) {
1788
+ this.search.removeClass("select2-active");
1789
+ return;
1790
+ }
1791
+
1792
+ // handle ajax error
1793
+ if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) {
1794
+ render("<li class='select2-ajax-error'>" + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + "</li>");
1795
+ return;
1796
+ }
1797
+
1798
+ // save context, if any
1799
+ this.context = (data.context===undefined) ? null : data.context;
1800
+ // create a default choice and prepend it to the list
1801
+ if (this.opts.createSearchChoice && search.val() !== "") {
1802
+ def = this.opts.createSearchChoice.call(self, search.val(), data.results);
1803
+ if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
1804
+ if ($(data.results).filter(
1805
+ function () {
1806
+ return equal(self.id(this), self.id(def));
1807
+ }).length === 0) {
1808
+ this.opts.createSearchChoicePosition(data.results, def);
1809
+ }
1810
+ }
1811
+ }
1812
+
1813
+ if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
1814
+ render("<li class='select2-no-results'>" + evaluate(opts.formatNoMatches, opts.element, search.val()) + "</li>");
1815
+ return;
1816
+ }
1817
+
1818
+ results.empty();
1819
+ self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
1820
+
1821
+ if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
1822
+ results.append("<li class='select2-more-results'>" + opts.escapeMarkup(evaluate(opts.formatLoadMore, opts.element, this.resultsPage)) + "</li>");
1823
+ window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
1824
+ }
1825
+
1826
+ this.postprocessResults(data, initial);
1827
+
1828
+ postRender();
1829
+
1830
+ this.opts.element.trigger({ type: "select2-loaded", items: data });
1831
+ })});
1832
+ },
1833
+
1834
+ // abstract
1835
+ cancel: function () {
1836
+ this.close();
1837
+ },
1838
+
1839
+ // abstract
1840
+ blur: function () {
1841
+ // if selectOnBlur == true, select the currently highlighted option
1842
+ if (this.opts.selectOnBlur)
1843
+ this.selectHighlighted({noFocus: true});
1844
+
1845
+ this.close();
1846
+ this.container.removeClass("select2-container-active");
1847
+ // synonymous to .is(':focus'), which is available in jquery >= 1.6
1848
+ if (this.search[0] === document.activeElement) { this.search.blur(); }
1849
+ this.clearSearch();
1850
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
1851
+ },
1852
+
1853
+ // abstract
1854
+ focusSearch: function () {
1855
+ focus(this.search);
1856
+ },
1857
+
1858
+ // abstract
1859
+ selectHighlighted: function (options) {
1860
+ if (this._touchMoved) {
1861
+ this.clearTouchMoved();
1862
+ return;
1863
+ }
1864
+ var index=this.highlight(),
1865
+ highlighted=this.results.find(".select2-highlighted"),
1866
+ data = highlighted.closest('.select2-result').data("select2-data");
1867
+
1868
+ if (data) {
1869
+ this.highlight(index);
1870
+ this.onSelect(data, options);
1871
+ } else if (options && options.noFocus) {
1872
+ this.close();
1873
+ }
1874
+ },
1875
+
1876
+ // abstract
1877
+ getPlaceholder: function () {
1878
+ var placeholderOption;
1879
+ return this.opts.element.attr("placeholder") ||
1880
+ this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
1881
+ this.opts.element.data("placeholder") ||
1882
+ this.opts.placeholder ||
1883
+ ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined);
1884
+ },
1885
+
1886
+ // abstract
1887
+ getPlaceholderOption: function() {
1888
+ if (this.select) {
1889
+ var firstOption = this.select.children('option').first();
1890
+ if (this.opts.placeholderOption !== undefined ) {
1891
+ //Determine the placeholder option based on the specified placeholderOption setting
1892
+ return (this.opts.placeholderOption === "first" && firstOption) ||
1893
+ (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select));
1894
+ } else if ($.trim(firstOption.text()) === "" && firstOption.val() === "") {
1895
+ //No explicit placeholder option specified, use the first if it's blank
1896
+ return firstOption;
1897
+ }
1898
+ }
1899
+ },
1900
+
1901
+ /**
1902
+ * Get the desired width for the container element. This is
1903
+ * derived first from option `width` passed to select2, then
1904
+ * the inline 'style' on the original element, and finally
1905
+ * falls back to the jQuery calculated element width.
1906
+ */
1907
+ // abstract
1908
+ initContainerWidth: function () {
1909
+ function resolveContainerWidth() {
1910
+ var style, attrs, matches, i, l, attr;
1911
+
1912
+ if (this.opts.width === "off") {
1913
+ return null;
1914
+ } else if (this.opts.width === "element"){
1915
+ return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
1916
+ } else if (this.opts.width === "copy" || this.opts.width === "resolve") {
1917
+ // check if there is inline style on the element that contains width
1918
+ style = this.opts.element.attr('style');
1919
+ if (style !== undefined) {
1920
+ attrs = style.split(';');
1921
+ for (i = 0, l = attrs.length; i < l; i = i + 1) {
1922
+ attr = attrs[i].replace(/\s/g, '');
1923
+ matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
1924
+ if (matches !== null && matches.length >= 1)
1925
+ return matches[1];
1926
+ }
1927
+ }
1928
+
1929
+ if (this.opts.width === "resolve") {
1930
+ // next check if css('width') can resolve a width that is percent based, this is sometimes possible
1931
+ // when attached to input type=hidden or elements hidden via css
1932
+ style = this.opts.element.css('width');
1933
+ if (style.indexOf("%") > 0) return style;
1934
+
1935
+ // finally, fallback on the calculated width of the element
1936
+ return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
1937
+ }
1938
+
1939
+ return null;
1940
+ } else if ($.isFunction(this.opts.width)) {
1941
+ return this.opts.width();
1942
+ } else {
1943
+ return this.opts.width;
1944
+ }
1945
+ };
1946
+
1947
+ var width = resolveContainerWidth.call(this);
1948
+ if (width !== null) {
1949
+ this.container.css("width", width);
1950
+ }
1951
+ }
1952
+ });
1953
+
1954
+ SingleSelect2 = clazz(AbstractSelect2, {
1955
+
1956
+ // single
1957
+
1958
+ createContainer: function () {
1959
+ var container = $(document.createElement("div")).attr({
1960
+ "class": "select2-container"
1961
+ }).html([
1962
+ "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>",
1963
+ " <span class='select2-chosen'>&#160;</span><abbr class='select2-search-choice-close'></abbr>",
1964
+ " <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>",
1965
+ "</a>",
1966
+ "<label for='' class='select2-offscreen'></label>",
1967
+ "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />",
1968
+ "<div class='select2-drop select2-display-none'>",
1969
+ " <div class='select2-search'>",
1970
+ " <label for='' class='select2-offscreen'></label>",
1971
+ " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'",
1972
+ " aria-autocomplete='list' />",
1973
+ " </div>",
1974
+ " <ul class='select2-results' role='listbox'>",
1975
+ " </ul>",
1976
+ "</div>"].join(""));
1977
+ return container;
1978
+ },
1979
+
1980
+ // single
1981
+ enableInterface: function() {
1982
+ if (this.parent.enableInterface.apply(this, arguments)) {
1983
+ this.focusser.prop("disabled", !this.isInterfaceEnabled());
1984
+ }
1985
+ },
1986
+
1987
+ // single
1988
+ opening: function () {
1989
+ var el, range, len;
1990
+
1991
+ if (this.opts.minimumResultsForSearch >= 0) {
1992
+ this.showSearch(true);
1993
+ }
1994
+
1995
+ this.parent.opening.apply(this, arguments);
1996
+
1997
+ if (this.showSearchInput !== false) {
1998
+ // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
1999
+ // all other browsers handle this just fine
2000
+
2001
+ this.search.val(this.focusser.val());
2002
+ }
2003
+ if (this.opts.shouldFocusInput(this)) {
2004
+ this.search.focus();
2005
+ // move the cursor to the end after focussing, otherwise it will be at the beginning and
2006
+ // new text will appear *before* focusser.val()
2007
+ el = this.search.get(0);
2008
+ if (el.createTextRange) {
2009
+ range = el.createTextRange();
2010
+ range.collapse(false);
2011
+ range.select();
2012
+ } else if (el.setSelectionRange) {
2013
+ len = this.search.val().length;
2014
+ el.setSelectionRange(len, len);
2015
+ }
2016
+ }
2017
+
2018
+ // initializes search's value with nextSearchTerm (if defined by user)
2019
+ // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
2020
+ if(this.search.val() === "") {
2021
+ if(this.nextSearchTerm != undefined){
2022
+ this.search.val(this.nextSearchTerm);
2023
+ this.search.select();
2024
+ }
2025
+ }
2026
+
2027
+ this.focusser.prop("disabled", true).val("");
2028
+ this.updateResults(true);
2029
+ this.opts.element.trigger($.Event("select2-open"));
2030
+ },
2031
+
2032
+ // single
2033
+ close: function () {
2034
+ if (!this.opened()) return;
2035
+ this.parent.close.apply(this, arguments);
2036
+
2037
+ this.focusser.prop("disabled", false);
2038
+
2039
+ if (this.opts.shouldFocusInput(this)) {
2040
+ this.focusser.focus();
2041
+ }
2042
+ },
2043
+
2044
+ // single
2045
+ focus: function () {
2046
+ if (this.opened()) {
2047
+ this.close();
2048
+ } else {
2049
+ this.focusser.prop("disabled", false);
2050
+ if (this.opts.shouldFocusInput(this)) {
2051
+ this.focusser.focus();
2052
+ }
2053
+ }
2054
+ },
2055
+
2056
+ // single
2057
+ isFocused: function () {
2058
+ return this.container.hasClass("select2-container-active");
2059
+ },
2060
+
2061
+ // single
2062
+ cancel: function () {
2063
+ this.parent.cancel.apply(this, arguments);
2064
+ this.focusser.prop("disabled", false);
2065
+
2066
+ if (this.opts.shouldFocusInput(this)) {
2067
+ this.focusser.focus();
2068
+ }
2069
+ },
2070
+
2071
+ // single
2072
+ destroy: function() {
2073
+ $("label[for='" + this.focusser.attr('id') + "']")
2074
+ .attr('for', this.opts.element.attr("id"));
2075
+ this.parent.destroy.apply(this, arguments);
2076
+
2077
+ cleanupJQueryElements.call(this,
2078
+ "selection",
2079
+ "focusser"
2080
+ );
2081
+ },
2082
+
2083
+ // single
2084
+ initContainer: function () {
2085
+
2086
+ var selection,
2087
+ container = this.container,
2088
+ dropdown = this.dropdown,
2089
+ idSuffix = nextUid(),
2090
+ elementLabel;
2091
+
2092
+ if (this.opts.minimumResultsForSearch < 0) {
2093
+ this.showSearch(false);
2094
+ } else {
2095
+ this.showSearch(true);
2096
+ }
2097
+
2098
+ this.selection = selection = container.find(".select2-choice");
2099
+
2100
+ this.focusser = container.find(".select2-focusser");
2101
+
2102
+ // add aria associations
2103
+ selection.find(".select2-chosen").attr("id", "select2-chosen-"+idSuffix);
2104
+ this.focusser.attr("aria-labelledby", "select2-chosen-"+idSuffix);
2105
+ this.results.attr("id", "select2-results-"+idSuffix);
2106
+ this.search.attr("aria-owns", "select2-results-"+idSuffix);
2107
+
2108
+ // rewrite labels from original element to focusser
2109
+ this.focusser.attr("id", "s2id_autogen"+idSuffix);
2110
+
2111
+ elementLabel = $("label[for='" + this.opts.element.attr("id") + "']");
2112
+ this.opts.element.focus(this.bind(function () { this.focus(); }));
2113
+
2114
+ this.focusser.prev()
2115
+ .text(elementLabel.text())
2116
+ .attr('for', this.focusser.attr('id'));
2117
+
2118
+ // Ensure the original element retains an accessible name
2119
+ var originalTitle = this.opts.element.attr("title");
2120
+ this.opts.element.attr("title", (originalTitle || elementLabel.text()));
2121
+
2122
+ this.focusser.attr("tabindex", this.elementTabIndex);
2123
+
2124
+ // write label for search field using the label from the focusser element
2125
+ this.search.attr("id", this.focusser.attr('id') + '_search');
2126
+
2127
+ this.search.prev()
2128
+ .text($("label[for='" + this.focusser.attr('id') + "']").text())
2129
+ .attr('for', this.search.attr('id'));
2130
+
2131
+ this.search.on("keydown", this.bind(function (e) {
2132
+ if (!this.isInterfaceEnabled()) return;
2133
+
2134
+ // filter 229 keyCodes (input method editor is processing key input)
2135
+ if (229 == e.keyCode) return;
2136
+
2137
+ if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
2138
+ // prevent the page from scrolling
2139
+ killEvent(e);
2140
+ return;
2141
+ }
2142
+
2143
+ switch (e.which) {
2144
+ case KEY.UP:
2145
+ case KEY.DOWN:
2146
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
2147
+ killEvent(e);
2148
+ return;
2149
+ case KEY.ENTER:
2150
+ this.selectHighlighted();
2151
+ killEvent(e);
2152
+ return;
2153
+ case KEY.TAB:
2154
+ this.selectHighlighted({noFocus: true});
2155
+ return;
2156
+ case KEY.ESC:
2157
+ this.cancel(e);
2158
+ killEvent(e);
2159
+ return;
2160
+ }
2161
+ }));
2162
+
2163
+ this.search.on("blur", this.bind(function(e) {
2164
+ // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
2165
+ // without this the search field loses focus which is annoying
2166
+ if (document.activeElement === this.body.get(0)) {
2167
+ window.setTimeout(this.bind(function() {
2168
+ if (this.opened()) {
2169
+ this.search.focus();
2170
+ }
2171
+ }), 0);
2172
+ }
2173
+ }));
2174
+
2175
+ this.focusser.on("keydown", this.bind(function (e) {
2176
+ if (!this.isInterfaceEnabled()) return;
2177
+
2178
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
2179
+ return;
2180
+ }
2181
+
2182
+ if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
2183
+ killEvent(e);
2184
+ return;
2185
+ }
2186
+
2187
+ if (e.which == KEY.DOWN || e.which == KEY.UP
2188
+ || (e.which == KEY.ENTER && this.opts.openOnEnter)) {
2189
+
2190
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
2191
+
2192
+ this.open();
2193
+ killEvent(e);
2194
+ return;
2195
+ }
2196
+
2197
+ if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) {
2198
+ if (this.opts.allowClear) {
2199
+ this.clear();
2200
+ }
2201
+ killEvent(e);
2202
+ return;
2203
+ }
2204
+ }));
2205
+
2206
+
2207
+ installKeyUpChangeEvent(this.focusser);
2208
+ this.focusser.on("keyup-change input", this.bind(function(e) {
2209
+ if (this.opts.minimumResultsForSearch >= 0) {
2210
+ e.stopPropagation();
2211
+ if (this.opened()) return;
2212
+ this.open();
2213
+ }
2214
+ }));
2215
+
2216
+ selection.on("mousedown touchstart", "abbr", this.bind(function (e) {
2217
+ if (!this.isInterfaceEnabled()) {
2218
+ return;
2219
+ }
2220
+
2221
+ this.clear();
2222
+ killEventImmediately(e);
2223
+ this.close();
2224
+
2225
+ if (this.selection) {
2226
+ this.selection.focus();
2227
+ }
2228
+ }));
2229
+
2230
+ selection.on("mousedown touchstart", this.bind(function (e) {
2231
+ // Prevent IE from generating a click event on the body
2232
+ reinsertElement(selection);
2233
+
2234
+ if (!this.container.hasClass("select2-container-active")) {
2235
+ this.opts.element.trigger($.Event("select2-focus"));
2236
+ }
2237
+
2238
+ if (this.opened()) {
2239
+ this.close();
2240
+ } else if (this.isInterfaceEnabled()) {
2241
+ this.open();
2242
+ }
2243
+
2244
+ killEvent(e);
2245
+ }));
2246
+
2247
+ dropdown.on("mousedown touchstart", this.bind(function() {
2248
+ if (this.opts.shouldFocusInput(this)) {
2249
+ this.search.focus();
2250
+ }
2251
+ }));
2252
+
2253
+ selection.on("focus", this.bind(function(e) {
2254
+ killEvent(e);
2255
+ }));
2256
+
2257
+ this.focusser.on("focus", this.bind(function(){
2258
+ if (!this.container.hasClass("select2-container-active")) {
2259
+ this.opts.element.trigger($.Event("select2-focus"));
2260
+ }
2261
+ this.container.addClass("select2-container-active");
2262
+ })).on("blur", this.bind(function() {
2263
+ if (!this.opened()) {
2264
+ this.container.removeClass("select2-container-active");
2265
+ this.opts.element.trigger($.Event("select2-blur"));
2266
+ }
2267
+ }));
2268
+ this.search.on("focus", this.bind(function(){
2269
+ if (!this.container.hasClass("select2-container-active")) {
2270
+ this.opts.element.trigger($.Event("select2-focus"));
2271
+ }
2272
+ this.container.addClass("select2-container-active");
2273
+ }));
2274
+
2275
+ this.initContainerWidth();
2276
+ this.opts.element.hide();
2277
+ this.setPlaceholder();
2278
+
2279
+ },
2280
+
2281
+ // single
2282
+ clear: function(triggerChange) {
2283
+ var data=this.selection.data("select2-data");
2284
+ if (data) { // guard against queued quick consecutive clicks
2285
+ var evt = $.Event("select2-clearing");
2286
+ this.opts.element.trigger(evt);
2287
+ if (evt.isDefaultPrevented()) {
2288
+ return;
2289
+ }
2290
+ var placeholderOption = this.getPlaceholderOption();
2291
+ this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
2292
+ this.selection.find(".select2-chosen").empty();
2293
+ this.selection.removeData("select2-data");
2294
+ this.setPlaceholder();
2295
+
2296
+ if (triggerChange !== false){
2297
+ this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
2298
+ this.triggerChange({removed:data});
2299
+ }
2300
+ }
2301
+ },
2302
+
2303
+ /**
2304
+ * Sets selection based on source element's value
2305
+ */
2306
+ // single
2307
+ initSelection: function () {
2308
+ var selected;
2309
+ if (this.isPlaceholderOptionSelected()) {
2310
+ this.updateSelection(null);
2311
+ this.close();
2312
+ this.setPlaceholder();
2313
+ } else {
2314
+ var self = this;
2315
+ this.opts.initSelection.call(null, this.opts.element, function(selected){
2316
+ if (selected !== undefined && selected !== null) {
2317
+ self.updateSelection(selected);
2318
+ self.close();
2319
+ self.setPlaceholder();
2320
+ self.nextSearchTerm = self.opts.nextSearchTerm(selected, self.search.val());
2321
+ }
2322
+ });
2323
+ }
2324
+ },
2325
+
2326
+ isPlaceholderOptionSelected: function() {
2327
+ var placeholderOption;
2328
+ if (this.getPlaceholder() === undefined) return false; // no placeholder specified so no option should be considered
2329
+ return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected"))
2330
+ || (this.opts.element.val() === "")
2331
+ || (this.opts.element.val() === undefined)
2332
+ || (this.opts.element.val() === null);
2333
+ },
2334
+
2335
+ // single
2336
+ prepareOpts: function () {
2337
+ var opts = this.parent.prepareOpts.apply(this, arguments),
2338
+ self=this;
2339
+
2340
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
2341
+ // install the selection initializer
2342
+ opts.initSelection = function (element, callback) {
2343
+ var selected = element.find("option").filter(function() { return this.selected && !this.disabled });
2344
+ // a single select box always has a value, no need to null check 'selected'
2345
+ callback(self.optionToData(selected));
2346
+ };
2347
+ } else if ("data" in opts) {
2348
+ // install default initSelection when applied to hidden input and data is local
2349
+ opts.initSelection = opts.initSelection || function (element, callback) {
2350
+ var id = element.val();
2351
+ //search in data by id, storing the actual matching item
2352
+ var match = null;
2353
+ opts.query({
2354
+ matcher: function(term, text, el){
2355
+ var is_match = equal(id, opts.id(el));
2356
+ if (is_match) {
2357
+ match = el;
2358
+ }
2359
+ return is_match;
2360
+ },
2361
+ callback: !$.isFunction(callback) ? $.noop : function() {
2362
+ callback(match);
2363
+ }
2364
+ });
2365
+ };
2366
+ }
2367
+
2368
+ return opts;
2369
+ },
2370
+
2371
+ // single
2372
+ getPlaceholder: function() {
2373
+ // if a placeholder is specified on a single select without a valid placeholder option ignore it
2374
+ if (this.select) {
2375
+ if (this.getPlaceholderOption() === undefined) {
2376
+ return undefined;
2377
+ }
2378
+ }
2379
+
2380
+ return this.parent.getPlaceholder.apply(this, arguments);
2381
+ },
2382
+
2383
+ // single
2384
+ setPlaceholder: function () {
2385
+ var placeholder = this.getPlaceholder();
2386
+
2387
+ if (this.isPlaceholderOptionSelected() && placeholder !== undefined) {
2388
+
2389
+ // check for a placeholder option if attached to a select
2390
+ if (this.select && this.getPlaceholderOption() === undefined) return;
2391
+
2392
+ this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder));
2393
+
2394
+ this.selection.addClass("select2-default");
2395
+
2396
+ this.container.removeClass("select2-allowclear");
2397
+ }
2398
+ },
2399
+
2400
+ // single
2401
+ postprocessResults: function (data, initial, noHighlightUpdate) {
2402
+ var selected = 0, self = this, showSearchInput = true;
2403
+
2404
+ // find the selected element in the result list
2405
+
2406
+ this.findHighlightableChoices().each2(function (i, elm) {
2407
+ if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
2408
+ selected = i;
2409
+ return false;
2410
+ }
2411
+ });
2412
+
2413
+ // and highlight it
2414
+ if (noHighlightUpdate !== false) {
2415
+ if (initial === true && selected >= 0) {
2416
+ this.highlight(selected);
2417
+ } else {
2418
+ this.highlight(0);
2419
+ }
2420
+ }
2421
+
2422
+ // hide the search box if this is the first we got the results and there are enough of them for search
2423
+
2424
+ if (initial === true) {
2425
+ var min = this.opts.minimumResultsForSearch;
2426
+ if (min >= 0) {
2427
+ this.showSearch(countResults(data.results) >= min);
2428
+ }
2429
+ }
2430
+ },
2431
+
2432
+ // single
2433
+ showSearch: function(showSearchInput) {
2434
+ if (this.showSearchInput === showSearchInput) return;
2435
+
2436
+ this.showSearchInput = showSearchInput;
2437
+
2438
+ this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
2439
+ this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
2440
+ //add "select2-with-searchbox" to the container if search box is shown
2441
+ $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
2442
+ },
2443
+
2444
+ // single
2445
+ onSelect: function (data, options) {
2446
+
2447
+ if (!this.triggerSelect(data)) { return; }
2448
+
2449
+ var old = this.opts.element.val(),
2450
+ oldData = this.data();
2451
+
2452
+ this.opts.element.val(this.id(data));
2453
+ this.updateSelection(data);
2454
+
2455
+ this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
2456
+
2457
+ this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
2458
+ this.close();
2459
+
2460
+ if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) {
2461
+ this.focusser.focus();
2462
+ }
2463
+
2464
+ if (!equal(old, this.id(data))) {
2465
+ this.triggerChange({ added: data, removed: oldData });
2466
+ }
2467
+ },
2468
+
2469
+ // single
2470
+ updateSelection: function (data) {
2471
+
2472
+ var container=this.selection.find(".select2-chosen"), formatted, cssClass;
2473
+
2474
+ this.selection.data("select2-data", data);
2475
+
2476
+ container.empty();
2477
+ if (data !== null) {
2478
+ formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
2479
+ }
2480
+ if (formatted !== undefined) {
2481
+ container.append(formatted);
2482
+ }
2483
+ cssClass=this.opts.formatSelectionCssClass(data, container);
2484
+ if (cssClass !== undefined) {
2485
+ container.addClass(cssClass);
2486
+ }
2487
+
2488
+ this.selection.removeClass("select2-default");
2489
+
2490
+ if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
2491
+ this.container.addClass("select2-allowclear");
2492
+ }
2493
+ },
2494
+
2495
+ // single
2496
+ val: function () {
2497
+ var val,
2498
+ triggerChange = false,
2499
+ data = null,
2500
+ self = this,
2501
+ oldData = this.data();
2502
+
2503
+ if (arguments.length === 0) {
2504
+ return this.opts.element.val();
2505
+ }
2506
+
2507
+ val = arguments[0];
2508
+
2509
+ if (arguments.length > 1) {
2510
+ triggerChange = arguments[1];
2511
+ }
2512
+
2513
+ if (this.select) {
2514
+ this.select
2515
+ .val(val)
2516
+ .find("option").filter(function() { return this.selected }).each2(function (i, elm) {
2517
+ data = self.optionToData(elm);
2518
+ return false;
2519
+ });
2520
+ this.updateSelection(data);
2521
+ this.setPlaceholder();
2522
+ if (triggerChange) {
2523
+ this.triggerChange({added: data, removed:oldData});
2524
+ }
2525
+ } else {
2526
+ // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
2527
+ if (!val && val !== 0) {
2528
+ this.clear(triggerChange);
2529
+ return;
2530
+ }
2531
+ if (this.opts.initSelection === undefined) {
2532
+ throw new Error("cannot call val() if initSelection() is not defined");
2533
+ }
2534
+ this.opts.element.val(val);
2535
+ this.opts.initSelection(this.opts.element, function(data){
2536
+ self.opts.element.val(!data ? "" : self.id(data));
2537
+ self.updateSelection(data);
2538
+ self.setPlaceholder();
2539
+ if (triggerChange) {
2540
+ self.triggerChange({added: data, removed:oldData});
2541
+ }
2542
+ });
2543
+ }
2544
+ },
2545
+
2546
+ // single
2547
+ clearSearch: function () {
2548
+ this.search.val("");
2549
+ this.focusser.val("");
2550
+ },
2551
+
2552
+ // single
2553
+ data: function(value) {
2554
+ var data,
2555
+ triggerChange = false;
2556
+
2557
+ if (arguments.length === 0) {
2558
+ data = this.selection.data("select2-data");
2559
+ if (data == undefined) data = null;
2560
+ return data;
2561
+ } else {
2562
+ if (arguments.length > 1) {
2563
+ triggerChange = arguments[1];
2564
+ }
2565
+ if (!value) {
2566
+ this.clear(triggerChange);
2567
+ } else {
2568
+ data = this.data();
2569
+ this.opts.element.val(!value ? "" : this.id(value));
2570
+ this.updateSelection(value);
2571
+ if (triggerChange) {
2572
+ this.triggerChange({added: value, removed:data});
2573
+ }
2574
+ }
2575
+ }
2576
+ }
2577
+ });
2578
+
2579
+ MultiSelect2 = clazz(AbstractSelect2, {
2580
+
2581
+ // multi
2582
+ createContainer: function () {
2583
+ var container = $(document.createElement("div")).attr({
2584
+ "class": "select2-container select2-container-multi"
2585
+ }).html([
2586
+ "<ul class='select2-choices'>",
2587
+ " <li class='select2-search-field'>",
2588
+ " <label for='' class='select2-offscreen'></label>",
2589
+ " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
2590
+ " </li>",
2591
+ "</ul>",
2592
+ "<div class='select2-drop select2-drop-multi select2-display-none'>",
2593
+ " <ul class='select2-results'>",
2594
+ " </ul>",
2595
+ "</div>"].join(""));
2596
+ return container;
2597
+ },
2598
+
2599
+ // multi
2600
+ prepareOpts: function () {
2601
+ var opts = this.parent.prepareOpts.apply(this, arguments),
2602
+ self=this;
2603
+
2604
+ // TODO validate placeholder is a string if specified
2605
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
2606
+ // install the selection initializer
2607
+ opts.initSelection = function (element, callback) {
2608
+
2609
+ var data = [];
2610
+
2611
+ element.find("option").filter(function() { return this.selected && !this.disabled }).each2(function (i, elm) {
2612
+ data.push(self.optionToData(elm));
2613
+ });
2614
+ callback(data);
2615
+ };
2616
+ } else if ("data" in opts) {
2617
+ // install default initSelection when applied to hidden input and data is local
2618
+ opts.initSelection = opts.initSelection || function (element, callback) {
2619
+ var ids = splitVal(element.val(), opts.separator, opts.transformVal);
2620
+ //search in data by array of ids, storing matching items in a list
2621
+ var matches = [];
2622
+ opts.query({
2623
+ matcher: function(term, text, el){
2624
+ var is_match = $.grep(ids, function(id) {
2625
+ return equal(id, opts.id(el));
2626
+ }).length;
2627
+ if (is_match) {
2628
+ matches.push(el);
2629
+ }
2630
+ return is_match;
2631
+ },
2632
+ callback: !$.isFunction(callback) ? $.noop : function() {
2633
+ // reorder matches based on the order they appear in the ids array because right now
2634
+ // they are in the order in which they appear in data array
2635
+ var ordered = [];
2636
+ for (var i = 0; i < ids.length; i++) {
2637
+ var id = ids[i];
2638
+ for (var j = 0; j < matches.length; j++) {
2639
+ var match = matches[j];
2640
+ if (equal(id, opts.id(match))) {
2641
+ ordered.push(match);
2642
+ matches.splice(j, 1);
2643
+ break;
2644
+ }
2645
+ }
2646
+ }
2647
+ callback(ordered);
2648
+ }
2649
+ });
2650
+ };
2651
+ }
2652
+
2653
+ return opts;
2654
+ },
2655
+
2656
+ // multi
2657
+ selectChoice: function (choice) {
2658
+
2659
+ var selected = this.container.find(".select2-search-choice-focus");
2660
+ if (selected.length && choice && choice[0] == selected[0]) {
2661
+
2662
+ } else {
2663
+ if (selected.length) {
2664
+ this.opts.element.trigger("choice-deselected", selected);
2665
+ }
2666
+ selected.removeClass("select2-search-choice-focus");
2667
+ if (choice && choice.length) {
2668
+ this.close();
2669
+ choice.addClass("select2-search-choice-focus");
2670
+ this.opts.element.trigger("choice-selected", choice);
2671
+ }
2672
+ }
2673
+ },
2674
+
2675
+ // multi
2676
+ destroy: function() {
2677
+ $("label[for='" + this.search.attr('id') + "']")
2678
+ .attr('for', this.opts.element.attr("id"));
2679
+ this.parent.destroy.apply(this, arguments);
2680
+
2681
+ cleanupJQueryElements.call(this,
2682
+ "searchContainer",
2683
+ "selection"
2684
+ );
2685
+ },
2686
+
2687
+ // multi
2688
+ initContainer: function () {
2689
+
2690
+ var selector = ".select2-choices", selection;
2691
+
2692
+ this.searchContainer = this.container.find(".select2-search-field");
2693
+ this.selection = selection = this.container.find(selector);
2694
+
2695
+ var _this = this;
2696
+ this.selection.on("click", ".select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)", function (e) {
2697
+ _this.search[0].focus();
2698
+ _this.selectChoice($(this));
2699
+ });
2700
+
2701
+ // rewrite labels from original element to focusser
2702
+ this.search.attr("id", "s2id_autogen"+nextUid());
2703
+
2704
+ this.search.prev()
2705
+ .text($("label[for='" + this.opts.element.attr("id") + "']").text())
2706
+ .attr('for', this.search.attr('id'));
2707
+ this.opts.element.focus(this.bind(function () { this.focus(); }));
2708
+
2709
+ this.search.on("input paste", this.bind(function() {
2710
+ if (this.search.attr('placeholder') && this.search.val().length == 0) return;
2711
+ if (!this.isInterfaceEnabled()) return;
2712
+ if (!this.opened()) {
2713
+ this.open();
2714
+ }
2715
+ }));
2716
+
2717
+ this.search.attr("tabindex", this.elementTabIndex);
2718
+
2719
+ this.keydowns = 0;
2720
+ this.search.on("keydown", this.bind(function (e) {
2721
+ if (!this.isInterfaceEnabled()) return;
2722
+
2723
+ ++this.keydowns;
2724
+ var selected = selection.find(".select2-search-choice-focus");
2725
+ var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
2726
+ var next = selected.next(".select2-search-choice:not(.select2-locked)");
2727
+ var pos = getCursorInfo(this.search);
2728
+
2729
+ if (selected.length &&
2730
+ (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
2731
+ var selectedChoice = selected;
2732
+ if (e.which == KEY.LEFT && prev.length) {
2733
+ selectedChoice = prev;
2734
+ }
2735
+ else if (e.which == KEY.RIGHT) {
2736
+ selectedChoice = next.length ? next : null;
2737
+ }
2738
+ else if (e.which === KEY.BACKSPACE) {
2739
+ if (this.unselect(selected.first())) {
2740
+ this.search.width(10);
2741
+ selectedChoice = prev.length ? prev : next;
2742
+ }
2743
+ } else if (e.which == KEY.DELETE) {
2744
+ if (this.unselect(selected.first())) {
2745
+ this.search.width(10);
2746
+ selectedChoice = next.length ? next : null;
2747
+ }
2748
+ } else if (e.which == KEY.ENTER) {
2749
+ selectedChoice = null;
2750
+ }
2751
+
2752
+ this.selectChoice(selectedChoice);
2753
+ killEvent(e);
2754
+ if (!selectedChoice || !selectedChoice.length) {
2755
+ this.open();
2756
+ }
2757
+ return;
2758
+ } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
2759
+ || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
2760
+
2761
+ this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
2762
+ killEvent(e);
2763
+ return;
2764
+ } else {
2765
+ this.selectChoice(null);
2766
+ }
2767
+
2768
+ if (this.opened()) {
2769
+ switch (e.which) {
2770
+ case KEY.UP:
2771
+ case KEY.DOWN:
2772
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
2773
+ killEvent(e);
2774
+ return;
2775
+ case KEY.ENTER:
2776
+ this.selectHighlighted();
2777
+ killEvent(e);
2778
+ return;
2779
+ case KEY.TAB:
2780
+ this.selectHighlighted({noFocus:true});
2781
+ this.close();
2782
+ return;
2783
+ case KEY.ESC:
2784
+ this.cancel(e);
2785
+ killEvent(e);
2786
+ return;
2787
+ }
2788
+ }
2789
+
2790
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
2791
+ || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
2792
+ return;
2793
+ }
2794
+
2795
+ if (e.which === KEY.ENTER) {
2796
+ if (this.opts.openOnEnter === false) {
2797
+ return;
2798
+ } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
2799
+ return;
2800
+ }
2801
+ }
2802
+
2803
+ this.open();
2804
+
2805
+ if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
2806
+ // prevent the page from scrolling
2807
+ killEvent(e);
2808
+ }
2809
+
2810
+ if (e.which === KEY.ENTER) {
2811
+ // prevent form from being submitted
2812
+ killEvent(e);
2813
+ }
2814
+
2815
+ }));
2816
+
2817
+ this.search.on("keyup", this.bind(function (e) {
2818
+ this.keydowns = 0;
2819
+ this.resizeSearch();
2820
+ })
2821
+ );
2822
+
2823
+ this.search.on("blur", this.bind(function(e) {
2824
+ this.container.removeClass("select2-container-active");
2825
+ this.search.removeClass("select2-focused");
2826
+ this.selectChoice(null);
2827
+ if (!this.opened()) this.clearSearch();
2828
+ e.stopImmediatePropagation();
2829
+ this.opts.element.trigger($.Event("select2-blur"));
2830
+ }));
2831
+
2832
+ this.container.on("click", selector, this.bind(function (e) {
2833
+ if (!this.isInterfaceEnabled()) return;
2834
+ if ($(e.target).closest(".select2-search-choice").length > 0) {
2835
+ // clicked inside a select2 search choice, do not open
2836
+ return;
2837
+ }
2838
+ this.selectChoice(null);
2839
+ this.clearPlaceholder();
2840
+ if (!this.container.hasClass("select2-container-active")) {
2841
+ this.opts.element.trigger($.Event("select2-focus"));
2842
+ }
2843
+ this.open();
2844
+ this.focusSearch();
2845
+ e.preventDefault();
2846
+ }));
2847
+
2848
+ this.container.on("focus", selector, this.bind(function () {
2849
+ if (!this.isInterfaceEnabled()) return;
2850
+ if (!this.container.hasClass("select2-container-active")) {
2851
+ this.opts.element.trigger($.Event("select2-focus"));
2852
+ }
2853
+ this.container.addClass("select2-container-active");
2854
+ this.dropdown.addClass("select2-drop-active");
2855
+ this.clearPlaceholder();
2856
+ }));
2857
+
2858
+ this.initContainerWidth();
2859
+ this.opts.element.hide();
2860
+
2861
+ // set the placeholder if necessary
2862
+ this.clearSearch();
2863
+ },
2864
+
2865
+ // multi
2866
+ enableInterface: function() {
2867
+ if (this.parent.enableInterface.apply(this, arguments)) {
2868
+ this.search.prop("disabled", !this.isInterfaceEnabled());
2869
+ }
2870
+ },
2871
+
2872
+ // multi
2873
+ initSelection: function () {
2874
+ var data;
2875
+ if (this.opts.element.val() === "" && this.opts.element.text() === "") {
2876
+ this.updateSelection([]);
2877
+ this.close();
2878
+ // set the placeholder if necessary
2879
+ this.clearSearch();
2880
+ }
2881
+ if (this.select || this.opts.element.val() !== "") {
2882
+ var self = this;
2883
+ this.opts.initSelection.call(null, this.opts.element, function(data){
2884
+ if (data !== undefined && data !== null) {
2885
+ self.updateSelection(data);
2886
+ self.close();
2887
+ // set the placeholder if necessary
2888
+ self.clearSearch();
2889
+ }
2890
+ });
2891
+ }
2892
+ },
2893
+
2894
+ // multi
2895
+ clearSearch: function () {
2896
+ var placeholder = this.getPlaceholder(),
2897
+ maxWidth = this.getMaxSearchWidth();
2898
+
2899
+ if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
2900
+ this.search.val(placeholder).addClass("select2-default");
2901
+ // stretch the search box to full width of the container so as much of the placeholder is visible as possible
2902
+ // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944
2903
+ this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
2904
+ } else {
2905
+ this.search.val("").width(10);
2906
+ }
2907
+ },
2908
+
2909
+ // multi
2910
+ clearPlaceholder: function () {
2911
+ if (this.search.hasClass("select2-default")) {
2912
+ this.search.val("").removeClass("select2-default");
2913
+ }
2914
+ },
2915
+
2916
+ // multi
2917
+ opening: function () {
2918
+ this.clearPlaceholder(); // should be done before super so placeholder is not used to search
2919
+ this.resizeSearch();
2920
+
2921
+ this.parent.opening.apply(this, arguments);
2922
+
2923
+ this.focusSearch();
2924
+
2925
+ // initializes search's value with nextSearchTerm (if defined by user)
2926
+ // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
2927
+ if(this.search.val() === "") {
2928
+ if(this.nextSearchTerm != undefined){
2929
+ this.search.val(this.nextSearchTerm);
2930
+ this.search.select();
2931
+ }
2932
+ }
2933
+
2934
+ this.updateResults(true);
2935
+ if (this.opts.shouldFocusInput(this)) {
2936
+ this.search.focus();
2937
+ }
2938
+ this.opts.element.trigger($.Event("select2-open"));
2939
+ },
2940
+
2941
+ // multi
2942
+ close: function () {
2943
+ if (!this.opened()) return;
2944
+ this.parent.close.apply(this, arguments);
2945
+ },
2946
+
2947
+ // multi
2948
+ focus: function () {
2949
+ this.close();
2950
+ this.search.focus();
2951
+ },
2952
+
2953
+ // multi
2954
+ isFocused: function () {
2955
+ return this.search.hasClass("select2-focused");
2956
+ },
2957
+
2958
+ // multi
2959
+ updateSelection: function (data) {
2960
+ var ids = [], filtered = [], self = this;
2961
+
2962
+ // filter out duplicates
2963
+ $(data).each(function () {
2964
+ if (indexOf(self.id(this), ids) < 0) {
2965
+ ids.push(self.id(this));
2966
+ filtered.push(this);
2967
+ }
2968
+ });
2969
+ data = filtered;
2970
+
2971
+ this.selection.find(".select2-search-choice").remove();
2972
+ $(data).each(function () {
2973
+ self.addSelectedChoice(this);
2974
+ });
2975
+ self.postprocessResults();
2976
+ },
2977
+
2978
+ // multi
2979
+ tokenize: function() {
2980
+ var input = this.search.val();
2981
+ input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts);
2982
+ if (input != null && input != undefined) {
2983
+ this.search.val(input);
2984
+ if (input.length > 0) {
2985
+ this.open();
2986
+ }
2987
+ }
2988
+
2989
+ },
2990
+
2991
+ // multi
2992
+ onSelect: function (data, options) {
2993
+
2994
+ if (!this.triggerSelect(data) || data.text === "") { return; }
2995
+
2996
+ this.addSelectedChoice(data);
2997
+
2998
+ this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
2999
+
3000
+ // keep track of the search's value before it gets cleared
3001
+ this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
3002
+
3003
+ this.clearSearch();
3004
+ this.updateResults();
3005
+
3006
+ if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true);
3007
+
3008
+ if (this.opts.closeOnSelect) {
3009
+ this.close();
3010
+ this.search.width(10);
3011
+ } else {
3012
+ if (this.countSelectableResults()>0) {
3013
+ this.search.width(10);
3014
+ this.resizeSearch();
3015
+ if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) {
3016
+ // if we reached max selection size repaint the results so choices
3017
+ // are replaced with the max selection reached message
3018
+ this.updateResults(true);
3019
+ } else {
3020
+ // initializes search's value with nextSearchTerm and update search result
3021
+ if(this.nextSearchTerm != undefined){
3022
+ this.search.val(this.nextSearchTerm);
3023
+ this.updateResults();
3024
+ this.search.select();
3025
+ }
3026
+ }
3027
+ this.positionDropdown();
3028
+ } else {
3029
+ // if nothing left to select close
3030
+ this.close();
3031
+ this.search.width(10);
3032
+ }
3033
+ }
3034
+
3035
+ // since its not possible to select an element that has already been
3036
+ // added we do not need to check if this is a new element before firing change
3037
+ this.triggerChange({ added: data });
3038
+
3039
+ if (!options || !options.noFocus)
3040
+ this.focusSearch();
3041
+ },
3042
+
3043
+ // multi
3044
+ cancel: function () {
3045
+ this.close();
3046
+ this.focusSearch();
3047
+ },
3048
+
3049
+ addSelectedChoice: function (data) {
3050
+ var enableChoice = !data.locked,
3051
+ enabledItem = $(
3052
+ "<li class='select2-search-choice'>" +
3053
+ " <div></div>" +
3054
+ " <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" +
3055
+ "</li>"),
3056
+ disabledItem = $(
3057
+ "<li class='select2-search-choice select2-locked'>" +
3058
+ "<div></div>" +
3059
+ "</li>");
3060
+ var choice = enableChoice ? enabledItem : disabledItem,
3061
+ id = this.id(data),
3062
+ val = this.getVal(),
3063
+ formatted,
3064
+ cssClass;
3065
+
3066
+ formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup);
3067
+ if (formatted != undefined) {
3068
+ choice.find("div").replaceWith($("<div></div>").html(formatted));
3069
+ }
3070
+ cssClass=this.opts.formatSelectionCssClass(data, choice.find("div"));
3071
+ if (cssClass != undefined) {
3072
+ choice.addClass(cssClass);
3073
+ }
3074
+
3075
+ if(enableChoice){
3076
+ choice.find(".select2-search-choice-close")
3077
+ .on("mousedown", killEvent)
3078
+ .on("click dblclick", this.bind(function (e) {
3079
+ if (!this.isInterfaceEnabled()) return;
3080
+
3081
+ this.unselect($(e.target));
3082
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
3083
+ killEvent(e);
3084
+ this.close();
3085
+ this.focusSearch();
3086
+ })).on("focus", this.bind(function () {
3087
+ if (!this.isInterfaceEnabled()) return;
3088
+ this.container.addClass("select2-container-active");
3089
+ this.dropdown.addClass("select2-drop-active");
3090
+ }));
3091
+ }
3092
+
3093
+ choice.data("select2-data", data);
3094
+ choice.insertBefore(this.searchContainer);
3095
+
3096
+ val.push(id);
3097
+ this.setVal(val);
3098
+ },
3099
+
3100
+ // multi
3101
+ unselect: function (selected) {
3102
+ var val = this.getVal(),
3103
+ data,
3104
+ index;
3105
+ selected = selected.closest(".select2-search-choice");
3106
+
3107
+ if (selected.length === 0) {
3108
+ throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
3109
+ }
3110
+
3111
+ data = selected.data("select2-data");
3112
+
3113
+ if (!data) {
3114
+ // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
3115
+ // and invoked on an element already removed
3116
+ return;
3117
+ }
3118
+
3119
+ var evt = $.Event("select2-removing");
3120
+ evt.val = this.id(data);
3121
+ evt.choice = data;
3122
+ this.opts.element.trigger(evt);
3123
+
3124
+ if (evt.isDefaultPrevented()) {
3125
+ return false;
3126
+ }
3127
+
3128
+ while((index = indexOf(this.id(data), val)) >= 0) {
3129
+ val.splice(index, 1);
3130
+ this.setVal(val);
3131
+ if (this.select) this.postprocessResults();
3132
+ }
3133
+
3134
+ selected.remove();
3135
+
3136
+ this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
3137
+ this.triggerChange({ removed: data });
3138
+
3139
+ return true;
3140
+ },
3141
+
3142
+ // multi
3143
+ postprocessResults: function (data, initial, noHighlightUpdate) {
3144
+ var val = this.getVal(),
3145
+ choices = this.results.find(".select2-result"),
3146
+ compound = this.results.find(".select2-result-with-children"),
3147
+ self = this;
3148
+
3149
+ choices.each2(function (i, choice) {
3150
+ var id = self.id(choice.data("select2-data"));
3151
+ if (indexOf(id, val) >= 0) {
3152
+ choice.addClass("select2-selected");
3153
+ // mark all children of the selected parent as selected
3154
+ choice.find(".select2-result-selectable").addClass("select2-selected");
3155
+ }
3156
+ });
3157
+
3158
+ compound.each2(function(i, choice) {
3159
+ // hide an optgroup if it doesn't have any selectable children
3160
+ if (!choice.is('.select2-result-selectable')
3161
+ && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) {
3162
+ choice.addClass("select2-selected");
3163
+ }
3164
+ });
3165
+
3166
+ if (this.highlight() == -1 && noHighlightUpdate !== false && this.opts.closeOnSelect === true){
3167
+ self.highlight(0);
3168
+ }
3169
+
3170
+ //If all results are chosen render formatNoMatches
3171
+ if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
3172
+ if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) {
3173
+ if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) {
3174
+ this.results.append("<li class='select2-no-results'>" + evaluate(self.opts.formatNoMatches, self.opts.element, self.search.val()) + "</li>");
3175
+ }
3176
+ }
3177
+ }
3178
+
3179
+ },
3180
+
3181
+ // multi
3182
+ getMaxSearchWidth: function() {
3183
+ return this.selection.width() - getSideBorderPadding(this.search);
3184
+ },
3185
+
3186
+ // multi
3187
+ resizeSearch: function () {
3188
+ var minimumWidth, left, maxWidth, containerLeft, searchWidth,
3189
+ sideBorderPadding = getSideBorderPadding(this.search);
3190
+
3191
+ minimumWidth = measureTextWidth(this.search) + 10;
3192
+
3193
+ left = this.search.offset().left;
3194
+
3195
+ maxWidth = this.selection.width();
3196
+ containerLeft = this.selection.offset().left;
3197
+
3198
+ searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
3199
+
3200
+ if (searchWidth < minimumWidth) {
3201
+ searchWidth = maxWidth - sideBorderPadding;
3202
+ }
3203
+
3204
+ if (searchWidth < 40) {
3205
+ searchWidth = maxWidth - sideBorderPadding;
3206
+ }
3207
+
3208
+ if (searchWidth <= 0) {
3209
+ searchWidth = minimumWidth;
3210
+ }
3211
+
3212
+ this.search.width(Math.floor(searchWidth));
3213
+ },
3214
+
3215
+ // multi
3216
+ getVal: function () {
3217
+ var val;
3218
+ if (this.select) {
3219
+ val = this.select.val();
3220
+ return val === null ? [] : val;
3221
+ } else {
3222
+ val = this.opts.element.val();
3223
+ return splitVal(val, this.opts.separator, this.opts.transformVal);
3224
+ }
3225
+ },
3226
+
3227
+ // multi
3228
+ setVal: function (val) {
3229
+ var unique;
3230
+ if (this.select) {
3231
+ this.select.val(val);
3232
+ } else {
3233
+ unique = [];
3234
+ // filter out duplicates
3235
+ $(val).each(function () {
3236
+ if (indexOf(this, unique) < 0) unique.push(this);
3237
+ });
3238
+ this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
3239
+ }
3240
+ },
3241
+
3242
+ // multi
3243
+ buildChangeDetails: function (old, current) {
3244
+ var current = current.slice(0),
3245
+ old = old.slice(0);
3246
+
3247
+ // remove intersection from each array
3248
+ for (var i = 0; i < current.length; i++) {
3249
+ for (var j = 0; j < old.length; j++) {
3250
+ if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
3251
+ current.splice(i, 1);
3252
+ if(i>0){
3253
+ i--;
3254
+ }
3255
+ old.splice(j, 1);
3256
+ j--;
3257
+ }
3258
+ }
3259
+ }
3260
+
3261
+ return {added: current, removed: old};
3262
+ },
3263
+
3264
+
3265
+ // multi
3266
+ val: function (val, triggerChange) {
3267
+ var oldData, self=this;
3268
+
3269
+ if (arguments.length === 0) {
3270
+ return this.getVal();
3271
+ }
3272
+
3273
+ oldData=this.data();
3274
+ if (!oldData.length) oldData=[];
3275
+
3276
+ // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
3277
+ if (!val && val !== 0) {
3278
+ this.opts.element.val("");
3279
+ this.updateSelection([]);
3280
+ this.clearSearch();
3281
+ if (triggerChange) {
3282
+ this.triggerChange({added: this.data(), removed: oldData});
3283
+ }
3284
+ return;
3285
+ }
3286
+
3287
+ // val is a list of ids
3288
+ this.setVal(val);
3289
+
3290
+ if (this.select) {
3291
+ this.opts.initSelection(this.select, this.bind(this.updateSelection));
3292
+ if (triggerChange) {
3293
+ this.triggerChange(this.buildChangeDetails(oldData, this.data()));
3294
+ }
3295
+ } else {
3296
+ if (this.opts.initSelection === undefined) {
3297
+ throw new Error("val() cannot be called if initSelection() is not defined");
3298
+ }
3299
+
3300
+ this.opts.initSelection(this.opts.element, function(data){
3301
+ var ids=$.map(data, self.id);
3302
+ self.setVal(ids);
3303
+ self.updateSelection(data);
3304
+ self.clearSearch();
3305
+ if (triggerChange) {
3306
+ self.triggerChange(self.buildChangeDetails(oldData, self.data()));
3307
+ }
3308
+ });
3309
+ }
3310
+ this.clearSearch();
3311
+ },
3312
+
3313
+ // multi
3314
+ onSortStart: function() {
3315
+ if (this.select) {
3316
+ throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
3317
+ }
3318
+
3319
+ // collapse search field into 0 width so its container can be collapsed as well
3320
+ this.search.width(0);
3321
+ // hide the container
3322
+ this.searchContainer.hide();
3323
+ },
3324
+
3325
+ // multi
3326
+ onSortEnd:function() {
3327
+
3328
+ var val=[], self=this;
3329
+
3330
+ // show search and move it to the end of the list
3331
+ this.searchContainer.show();
3332
+ // make sure the search container is the last item in the list
3333
+ this.searchContainer.appendTo(this.searchContainer.parent());
3334
+ // since we collapsed the width in dragStarted, we resize it here
3335
+ this.resizeSearch();
3336
+
3337
+ // update selection
3338
+ this.selection.find(".select2-search-choice").each(function() {
3339
+ val.push(self.opts.id($(this).data("select2-data")));
3340
+ });
3341
+ this.setVal(val);
3342
+ this.triggerChange();
3343
+ },
3344
+
3345
+ // multi
3346
+ data: function(values, triggerChange) {
3347
+ var self=this, ids, old;
3348
+ if (arguments.length === 0) {
3349
+ return this.selection
3350
+ .children(".select2-search-choice")
3351
+ .map(function() { return $(this).data("select2-data"); })
3352
+ .get();
3353
+ } else {
3354
+ old = this.data();
3355
+ if (!values) { values = []; }
3356
+ ids = $.map(values, function(e) { return self.opts.id(e); });
3357
+ this.setVal(ids);
3358
+ this.updateSelection(values);
3359
+ this.clearSearch();
3360
+ if (triggerChange) {
3361
+ this.triggerChange(this.buildChangeDetails(old, this.data()));
3362
+ }
3363
+ }
3364
+ }
3365
+ });
3366
+
3367
+ $.fn.select2 = function () {
3368
+
3369
+ var args = Array.prototype.slice.call(arguments, 0),
3370
+ opts,
3371
+ select2,
3372
+ method, value, multiple,
3373
+ allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"],
3374
+ valueMethods = ["opened", "isFocused", "container", "dropdown"],
3375
+ propertyMethods = ["val", "data"],
3376
+ methodsMap = { search: "externalSearch" };
3377
+
3378
+ this.each(function () {
3379
+ if (args.length === 0 || typeof(args[0]) === "object") {
3380
+ opts = args.length === 0 ? {} : $.extend({}, args[0]);
3381
+ opts.element = $(this);
3382
+
3383
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
3384
+ multiple = opts.element.prop("multiple");
3385
+ } else {
3386
+ multiple = opts.multiple || false;
3387
+ if ("tags" in opts) {opts.multiple = multiple = true;}
3388
+ }
3389
+
3390
+ select2 = multiple ? new window.Select2["class"].multi() : new window.Select2["class"].single();
3391
+ select2.init(opts);
3392
+ } else if (typeof(args[0]) === "string") {
3393
+
3394
+ if (indexOf(args[0], allowedMethods) < 0) {
3395
+ throw "Unknown method: " + args[0];
3396
+ }
3397
+
3398
+ value = undefined;
3399
+ select2 = $(this).data("select2");
3400
+ if (select2 === undefined) return;
3401
+
3402
+ method=args[0];
3403
+
3404
+ if (method === "container") {
3405
+ value = select2.container;
3406
+ } else if (method === "dropdown") {
3407
+ value = select2.dropdown;
3408
+ } else {
3409
+ if (methodsMap[method]) method = methodsMap[method];
3410
+
3411
+ value = select2[method].apply(select2, args.slice(1));
3412
+ }
3413
+ if (indexOf(args[0], valueMethods) >= 0
3414
+ || (indexOf(args[0], propertyMethods) >= 0 && args.length == 1)) {
3415
+ return false; // abort the iteration, ready to return first matched value
3416
+ }
3417
+ } else {
3418
+ throw "Invalid arguments to select2 plugin: " + args;
3419
+ }
3420
+ });
3421
+ return (value === undefined) ? this : value;
3422
+ };
3423
+
3424
+ // plugin defaults, accessible to users
3425
+ $.fn.select2.defaults = {
3426
+ width: "copy",
3427
+ loadMorePadding: 0,
3428
+ closeOnSelect: true,
3429
+ openOnEnter: true,
3430
+ containerCss: {},
3431
+ dropdownCss: {},
3432
+ containerCssClass: "",
3433
+ dropdownCssClass: "",
3434
+ formatResult: function(result, container, query, escapeMarkup) {
3435
+ var markup=[];
3436
+ markMatch(this.text(result), query.term, markup, escapeMarkup);
3437
+ return markup.join("");
3438
+ },
3439
+ transformVal: function(val) {
3440
+ return $.trim(val);
3441
+ },
3442
+ formatSelection: function (data, container, escapeMarkup) {
3443
+ return data ? escapeMarkup(this.text(data)) : undefined;
3444
+ },
3445
+ sortResults: function (results, container, query) {
3446
+ return results;
3447
+ },
3448
+ formatResultCssClass: function(data) {return data.css;},
3449
+ formatSelectionCssClass: function(data, container) {return undefined;},
3450
+ minimumResultsForSearch: 0,
3451
+ minimumInputLength: 0,
3452
+ maximumInputLength: null,
3453
+ maximumSelectionSize: 0,
3454
+ id: function (e) { return e == undefined ? null : e.id; },
3455
+ text: function (e) {
3456
+ if (e && this.data && this.data.text) {
3457
+ if ($.isFunction(this.data.text)) {
3458
+ return this.data.text(e);
3459
+ } else {
3460
+ return e[this.data.text];
3461
+ }
3462
+ } else {
3463
+ return e.text;
3464
+ }
3465
+ },
3466
+ matcher: function(term, text) {
3467
+ return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
3468
+ },
3469
+ separator: ",",
3470
+ tokenSeparators: [],
3471
+ tokenizer: defaultTokenizer,
3472
+ escapeMarkup: defaultEscapeMarkup,
3473
+ blurOnChange: false,
3474
+ selectOnBlur: false,
3475
+ adaptContainerCssClass: function(c) { return c; },
3476
+ adaptDropdownCssClass: function(c) { return null; },
3477
+ nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; },
3478
+ searchInputPlaceholder: '',
3479
+ createSearchChoicePosition: 'top',
3480
+ shouldFocusInput: function (instance) {
3481
+ // Attempt to detect touch devices
3482
+ var supportsTouchEvents = (('ontouchstart' in window) ||
3483
+ (navigator.msMaxTouchPoints > 0));
3484
+
3485
+ // Only devices which support touch events should be special cased
3486
+ if (!supportsTouchEvents) {
3487
+ return true;
3488
+ }
3489
+
3490
+ // Never focus the input if search is disabled
3491
+ if (instance.opts.minimumResultsForSearch < 0) {
3492
+ return false;
3493
+ }
3494
+
3495
+ return true;
3496
+ }
3497
+ };
3498
+
3499
+ $.fn.select2.locales = [];
3500
+
3501
+ $.fn.select2.locales['en'] = {
3502
+ formatMatches: function (matches) { if (matches === 1) { return "One result is available, press enter to select it."; } return matches + " results are available, use up and down arrow keys to navigate."; },
3503
+ formatNoMatches: function () { return "No matches found"; },
3504
+ formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; },
3505
+ formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); },
3506
+ formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
3507
+ formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
3508
+ formatLoadMore: function (pageNumber) { return "Loading more results…"; },
3509
+ formatSearching: function () { return "Searching…"; }
3510
+ };
3511
+
3512
+ $.extend($.fn.select2.defaults, $.fn.select2.locales['en']);
3513
+
3514
+ $.fn.select2.ajaxDefaults = {
3515
+ transport: $.ajax,
3516
+ params: {
3517
+ type: "GET",
3518
+ cache: false,
3519
+ dataType: "json"
3520
+ }
3521
+ };
3522
+
3523
+ // exports
3524
+ window.Select2 = {
3525
+ query: {
3526
+ ajax: ajax,
3527
+ local: local,
3528
+ tags: tags
3529
+ }, util: {
3530
+ debounce: debounce,
3531
+ markMatch: markMatch,
3532
+ escapeMarkup: defaultEscapeMarkup,
3533
+ stripDiacritics: stripDiacritics
3534
+ }, "class": {
3535
+ "abstract": AbstractSelect2,
3536
+ "single": SingleSelect2,
3537
+ "multi": MultiSelect2
3538
+ }
3539
+ };
3540
+
3541
+ }(jQuery));
lib/select2/select2.min.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Copyright 2014 Igor Vaynberg
3
+
4
+ Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014
5
+
6
+ This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
7
+ General Public License version 2 (the "GPL License"). You may choose either license to govern your
8
+ use of this software only upon the condition that you accept all of the terms of either the Apache
9
+ License or the GPL License.
10
+
11
+ You may obtain a copy of the Apache License and the GPL License at:
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+ http://www.gnu.org/licenses/gpl-2.0.html
15
+
16
+ Unless required by applicable law or agreed to in writing, software distributed under the Apache License
17
+ or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
18
+ either express or implied. See the Apache License and the GPL License for the specific language governing
19
+ permissions and limitations under the Apache License and the GPL License.
20
+ */
21
+ !function(a){"undefined"==typeof a.fn.each2&&a.extend(a.fn,{each2:function(b){for(var c=a([0]),d=-1,e=this.length;++d<e&&(c.context=c[0]=this[d])&&b.call(c[0],d,c)!==!1;);return this}})}(jQuery),function(a,b){"use strict";function n(b){var c=a(document.createTextNode(""));b.before(c),c.before(b),c.remove()}function o(a){function b(a){return m[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function p(a,b){for(var c=0,d=b.length;d>c;c+=1)if(r(a,b[c]))return c;return-1}function q(){var b=a(l);b.appendTo(document.body);var c={width:b.width()-b[0].clientWidth,height:b.height()-b[0].clientHeight};return b.remove(),c}function r(a,c){return a===c?!0:a===b||c===b?!1:null===a||null===c?!1:a.constructor===String?a+""==c+"":c.constructor===String?c+""==a+"":!1}function s(a,b,c){var d,e,f;if(null===a||a.length<1)return[];for(d=a.split(b),e=0,f=d.length;f>e;e+=1)d[e]=c(d[e]);return d}function t(a){return a.outerWidth(!1)-a.width()}function u(c){var d="keyup-change-value";c.on("keydown",function(){a.data(c,d)===b&&a.data(c,d,c.val())}),c.on("keyup",function(){var e=a.data(c,d);e!==b&&c.val()!==e&&(a.removeData(c,d),c.trigger("keyup-change"))})}function v(c){c.on("mousemove",function(c){var d=h;(d===b||d.x!==c.pageX||d.y!==c.pageY)&&a(c.target).trigger("mousemove-filtered",c)})}function w(a,c,d){d=d||b;var e;return function(){var b=arguments;window.clearTimeout(e),e=window.setTimeout(function(){c.apply(d,b)},a)}}function x(a,b){var c=w(a,function(a){b.trigger("scroll-debounced",a)});b.on("scroll",function(a){p(a.target,b.get())>=0&&c(a)})}function y(a){a[0]!==document.activeElement&&window.setTimeout(function(){var d,b=a[0],c=a.val().length;a.focus();var e=b.offsetWidth>0||b.offsetHeight>0;e&&b===document.activeElement&&(b.setSelectionRange?b.setSelectionRange(c,c):b.createTextRange&&(d=b.createTextRange(),d.collapse(!1),d.select()))},0)}function z(b){b=a(b)[0];var c=0,d=0;if("selectionStart"in b)c=b.selectionStart,d=b.selectionEnd-c;else if("selection"in document){b.focus();var e=document.selection.createRange();d=document.selection.createRange().text.length,e.moveStart("character",-b.value.length),c=e.text.length-d}return{offset:c,length:d}}function A(a){a.preventDefault(),a.stopPropagation()}function B(a){a.preventDefault(),a.stopImmediatePropagation()}function C(b){if(!g){var c=b[0].currentStyle||window.getComputedStyle(b[0],null);g=a(document.createElement("div")).css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),g.attr("class","select2-sizer"),a(document.body).append(g)}return g.text(b.val()),g.width()}function D(b,c,d){var e,g,f=[];e=a.trim(b.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each2(function(){0===this.indexOf("select2-")&&f.push(this)})),e=a.trim(c.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each2(function(){0!==this.indexOf("select2-")&&(g=d(this),g&&f.push(g))})),b.attr("class",f.join(" "))}function E(a,b,c,d){var e=o(a.toUpperCase()).indexOf(o(b.toUpperCase())),f=b.length;return 0>e?(c.push(d(a)),void 0):(c.push(d(a.substring(0,e))),c.push("<span class='select2-match'>"),c.push(d(a.substring(e,e+f))),c.push("</span>"),c.push(d(a.substring(e+f,a.length))),void 0)}function F(a){var b={"\\":"&#92;","&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#47;"};return String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})}function G(c){var d,e=null,f=c.quietMillis||100,g=c.url,h=this;return function(i){window.clearTimeout(d),d=window.setTimeout(function(){var d=c.data,f=g,j=c.transport||a.fn.select2.ajaxDefaults.transport,k={type:c.type||"GET",cache:c.cache||!1,jsonpCallback:c.jsonpCallback||b,dataType:c.dataType||"json"},l=a.extend({},a.fn.select2.ajaxDefaults.params,k);d=d?d.call(h,i.term,i.page,i.context):null,f="function"==typeof f?f.call(h,i.term,i.page,i.context):f,e&&"function"==typeof e.abort&&e.abort(),c.params&&(a.isFunction(c.params)?a.extend(l,c.params.call(h)):a.extend(l,c.params)),a.extend(l,{url:f,dataType:c.dataType,data:d,success:function(a){var b=c.results(a,i.page,i);i.callback(b)},error:function(a,b,c){var d={hasError:!0,jqXHR:a,textStatus:b,errorThrown:c};i.callback(d)}}),e=j.call(h,l)},f)}}function H(b){var d,e,c=b,f=function(a){return""+a.text};a.isArray(c)&&(e=c,c={results:e}),a.isFunction(c)===!1&&(e=c,c=function(){return e});var g=c();return g.text&&(f=g.text,a.isFunction(f)||(d=g.text,f=function(a){return a[d]})),function(b){var g,d=b.term,e={results:[]};return""===d?(b.callback(c()),void 0):(g=function(c,e){var h,i;if(c=c[0],c.children){h={};for(i in c)c.hasOwnProperty(i)&&(h[i]=c[i]);h.children=[],a(c.children).each2(function(a,b){g(b,h.children)}),(h.children.length||b.matcher(d,f(h),c))&&e.push(h)}else b.matcher(d,f(c),c)&&e.push(c)},a(c().results).each2(function(a,b){g(b,e.results)}),b.callback(e),void 0)}}function I(c){var d=a.isFunction(c);return function(e){var f=e.term,g={results:[]},h=d?c(e):c;a.isArray(h)&&(a(h).each(function(){var a=this.text!==b,c=a?this.text:this;(""===f||e.matcher(f,c))&&g.results.push(a?this:{id:this,text:this})}),e.callback(g))}}function J(b,c){if(a.isFunction(b))return!0;if(!b)return!1;if("string"==typeof b)return!0;throw new Error(c+" must be a string, function, or falsy value")}function K(b,c){if(a.isFunction(b)){var d=Array.prototype.slice.call(arguments,2);return b.apply(c,d)}return b}function L(b){var c=0;return a.each(b,function(a,b){b.children?c+=L(b.children):c++}),c}function M(a,c,d,e){var h,i,j,k,l,f=a,g=!1;if(!e.createSearchChoice||!e.tokenSeparators||e.tokenSeparators.length<1)return b;for(;;){for(i=-1,j=0,k=e.tokenSeparators.length;k>j&&(l=e.tokenSeparators[j],i=a.indexOf(l),!(i>=0));j++);if(0>i)break;if(h=a.substring(0,i),a=a.substring(i+l.length),h.length>0&&(h=e.createSearchChoice.call(this,h,c),h!==b&&null!==h&&e.id(h)!==b&&null!==e.id(h))){for(g=!1,j=0,k=c.length;k>j;j++)if(r(e.id(h),e.id(c[j]))){g=!0;break}g||d(h)}}return f!==a?a:void 0}function N(){var b=this;a.each(arguments,function(a,c){b[c].remove(),b[c]=null})}function O(b,c){var d=function(){};return d.prototype=new b,d.prototype.constructor=d,d.prototype.parent=b.prototype,d.prototype=a.extend(d.prototype,c),d}if(window.Select2===b){var c,d,e,f,g,i,j,h={x:0,y:0},k={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case k.LEFT:case k.RIGHT:case k.UP:case k.DOWN:return!0}return!1},isControl:function(a){var b=a.which;switch(b){case k.SHIFT:case k.CTRL:case k.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){return a=a.which?a.which:a,a>=112&&123>=a}},l="<div class='select2-measure-scrollbar'></div>",m={"\u24b6":"A","\uff21":"A","\xc0":"A","\xc1":"A","\xc2":"A","\u1ea6":"A","\u1ea4":"A","\u1eaa":"A","\u1ea8":"A","\xc3":"A","\u0100":"A","\u0102":"A","\u1eb0":"A","\u1eae":"A","\u1eb4":"A","\u1eb2":"A","\u0226":"A","\u01e0":"A","\xc4":"A","\u01de":"A","\u1ea2":"A","\xc5":"A","\u01fa":"A","\u01cd":"A","\u0200":"A","\u0202":"A","\u1ea0":"A","\u1eac":"A","\u1eb6":"A","\u1e00":"A","\u0104":"A","\u023a":"A","\u2c6f":"A","\ua732":"AA","\xc6":"AE","\u01fc":"AE","\u01e2":"AE","\ua734":"AO","\ua736":"AU","\ua738":"AV","\ua73a":"AV","\ua73c":"AY","\u24b7":"B","\uff22":"B","\u1e02":"B","\u1e04":"B","\u1e06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24b8":"C","\uff23":"C","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\xc7":"C","\u1e08":"C","\u0187":"C","\u023b":"C","\ua73e":"C","\u24b9":"D","\uff24":"D","\u1e0a":"D","\u010e":"D","\u1e0c":"D","\u1e10":"D","\u1e12":"D","\u1e0e":"D","\u0110":"D","\u018b":"D","\u018a":"D","\u0189":"D","\ua779":"D","\u01f1":"DZ","\u01c4":"DZ","\u01f2":"Dz","\u01c5":"Dz","\u24ba":"E","\uff25":"E","\xc8":"E","\xc9":"E","\xca":"E","\u1ec0":"E","\u1ebe":"E","\u1ec4":"E","\u1ec2":"E","\u1ebc":"E","\u0112":"E","\u1e14":"E","\u1e16":"E","\u0114":"E","\u0116":"E","\xcb":"E","\u1eba":"E","\u011a":"E","\u0204":"E","\u0206":"E","\u1eb8":"E","\u1ec6":"E","\u0228":"E","\u1e1c":"E","\u0118":"E","\u1e18":"E","\u1e1a":"E","\u0190":"E","\u018e":"E","\u24bb":"F","\uff26":"F","\u1e1e":"F","\u0191":"F","\ua77b":"F","\u24bc":"G","\uff27":"G","\u01f4":"G","\u011c":"G","\u1e20":"G","\u011e":"G","\u0120":"G","\u01e6":"G","\u0122":"G","\u01e4":"G","\u0193":"G","\ua7a0":"G","\ua77d":"G","\ua77e":"G","\u24bd":"H","\uff28":"H","\u0124":"H","\u1e22":"H","\u1e26":"H","\u021e":"H","\u1e24":"H","\u1e28":"H","\u1e2a":"H","\u0126":"H","\u2c67":"H","\u2c75":"H","\ua78d":"H","\u24be":"I","\uff29":"I","\xcc":"I","\xcd":"I","\xce":"I","\u0128":"I","\u012a":"I","\u012c":"I","\u0130":"I","\xcf":"I","\u1e2e":"I","\u1ec8":"I","\u01cf":"I","\u0208":"I","\u020a":"I","\u1eca":"I","\u012e":"I","\u1e2c":"I","\u0197":"I","\u24bf":"J","\uff2a":"J","\u0134":"J","\u0248":"J","\u24c0":"K","\uff2b":"K","\u1e30":"K","\u01e8":"K","\u1e32":"K","\u0136":"K","\u1e34":"K","\u0198":"K","\u2c69":"K","\ua740":"K","\ua742":"K","\ua744":"K","\ua7a2":"K","\u24c1":"L","\uff2c":"L","\u013f":"L","\u0139":"L","\u013d":"L","\u1e36":"L","\u1e38":"L","\u013b":"L","\u1e3c":"L","\u1e3a":"L","\u0141":"L","\u023d":"L","\u2c62":"L","\u2c60":"L","\ua748":"L","\ua746":"L","\ua780":"L","\u01c7":"LJ","\u01c8":"Lj","\u24c2":"M","\uff2d":"M","\u1e3e":"M","\u1e40":"M","\u1e42":"M","\u2c6e":"M","\u019c":"M","\u24c3":"N","\uff2e":"N","\u01f8":"N","\u0143":"N","\xd1":"N","\u1e44":"N","\u0147":"N","\u1e46":"N","\u0145":"N","\u1e4a":"N","\u1e48":"N","\u0220":"N","\u019d":"N","\ua790":"N","\ua7a4":"N","\u01ca":"NJ","\u01cb":"Nj","\u24c4":"O","\uff2f":"O","\xd2":"O","\xd3":"O","\xd4":"O","\u1ed2":"O","\u1ed0":"O","\u1ed6":"O","\u1ed4":"O","\xd5":"O","\u1e4c":"O","\u022c":"O","\u1e4e":"O","\u014c":"O","\u1e50":"O","\u1e52":"O","\u014e":"O","\u022e":"O","\u0230":"O","\xd6":"O","\u022a":"O","\u1ece":"O","\u0150":"O","\u01d1":"O","\u020c":"O","\u020e":"O","\u01a0":"O","\u1edc":"O","\u1eda":"O","\u1ee0":"O","\u1ede":"O","\u1ee2":"O","\u1ecc":"O","\u1ed8":"O","\u01ea":"O","\u01ec":"O","\xd8":"O","\u01fe":"O","\u0186":"O","\u019f":"O","\ua74a":"O","\ua74c":"O","\u01a2":"OI","\ua74e":"OO","\u0222":"OU","\u24c5":"P","\uff30":"P","\u1e54":"P","\u1e56":"P","\u01a4":"P","\u2c63":"P","\ua750":"P","\ua752":"P","\ua754":"P","\u24c6":"Q","\uff31":"Q","\ua756":"Q","\ua758":"Q","\u024a":"Q","\u24c7":"R","\uff32":"R","\u0154":"R","\u1e58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1e5a":"R","\u1e5c":"R","\u0156":"R","\u1e5e":"R","\u024c":"R","\u2c64":"R","\ua75a":"R","\ua7a6":"R","\ua782":"R","\u24c8":"S","\uff33":"S","\u1e9e":"S","\u015a":"S","\u1e64":"S","\u015c":"S","\u1e60":"S","\u0160":"S","\u1e66":"S","\u1e62":"S","\u1e68":"S","\u0218":"S","\u015e":"S","\u2c7e":"S","\ua7a8":"S","\ua784":"S","\u24c9":"T","\uff34":"T","\u1e6a":"T","\u0164":"T","\u1e6c":"T","\u021a":"T","\u0162":"T","\u1e70":"T","\u1e6e":"T","\u0166":"T","\u01ac":"T","\u01ae":"T","\u023e":"T","\ua786":"T","\ua728":"TZ","\u24ca":"U","\uff35":"U","\xd9":"U","\xda":"U","\xdb":"U","\u0168":"U","\u1e78":"U","\u016a":"U","\u1e7a":"U","\u016c":"U","\xdc":"U","\u01db":"U","\u01d7":"U","\u01d5":"U","\u01d9":"U","\u1ee6":"U","\u016e":"U","\u0170":"U","\u01d3":"U","\u0214":"U","\u0216":"U","\u01af":"U","\u1eea":"U","\u1ee8":"U","\u1eee":"U","\u1eec":"U","\u1ef0":"U","\u1ee4":"U","\u1e72":"U","\u0172":"U","\u1e76":"U","\u1e74":"U","\u0244":"U","\u24cb":"V","\uff36":"V","\u1e7c":"V","\u1e7e":"V","\u01b2":"V","\ua75e":"V","\u0245":"V","\ua760":"VY","\u24cc":"W","\uff37":"W","\u1e80":"W","\u1e82":"W","\u0174":"W","\u1e86":"W","\u1e84":"W","\u1e88":"W","\u2c72":"W","\u24cd":"X","\uff38":"X","\u1e8a":"X","\u1e8c":"X","\u24ce":"Y","\uff39":"Y","\u1ef2":"Y","\xdd":"Y","\u0176":"Y","\u1ef8":"Y","\u0232":"Y","\u1e8e":"Y","\u0178":"Y","\u1ef6":"Y","\u1ef4":"Y","\u01b3":"Y","\u024e":"Y","\u1efe":"Y","\u24cf":"Z","\uff3a":"Z","\u0179":"Z","\u1e90":"Z","\u017b":"Z","\u017d":"Z","\u1e92":"Z","\u1e94":"Z","\u01b5":"Z","\u0224":"Z","\u2c7f":"Z","\u2c6b":"Z","\ua762":"Z","\u24d0":"a","\uff41":"a","\u1e9a":"a","\xe0":"a","\xe1":"a","\xe2":"a","\u1ea7":"a","\u1ea5":"a","\u1eab":"a","\u1ea9":"a","\xe3":"a","\u0101":"a","\u0103":"a","\u1eb1":"a","\u1eaf":"a","\u1eb5":"a","\u1eb3":"a","\u0227":"a","\u01e1":"a","\xe4":"a","\u01df":"a","\u1ea3":"a","\xe5":"a","\u01fb":"a","\u01ce":"a","\u0201":"a","\u0203":"a","\u1ea1":"a","\u1ead":"a","\u1eb7":"a","\u1e01":"a","\u0105":"a","\u2c65":"a","\u0250":"a","\ua733":"aa","\xe6":"ae","\u01fd":"ae","\u01e3":"ae","\ua735":"ao","\ua737":"au","\ua739":"av","\ua73b":"av","\ua73d":"ay","\u24d1":"b","\uff42":"b","\u1e03":"b","\u1e05":"b","\u1e07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24d2":"c","\uff43":"c","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\xe7":"c","\u1e09":"c","\u0188":"c","\u023c":"c","\ua73f":"c","\u2184":"c","\u24d3":"d","\uff44":"d","\u1e0b":"d","\u010f":"d","\u1e0d":"d","\u1e11":"d","\u1e13":"d","\u1e0f":"d","\u0111":"d","\u018c":"d","\u0256":"d","\u0257":"d","\ua77a":"d","\u01f3":"dz","\u01c6":"dz","\u24d4":"e","\uff45":"e","\xe8":"e","\xe9":"e","\xea":"e","\u1ec1":"e","\u1ebf":"e","\u1ec5":"e","\u1ec3":"e","\u1ebd":"e","\u0113":"e","\u1e15":"e","\u1e17":"e","\u0115":"e","\u0117":"e","\xeb":"e","\u1ebb":"e","\u011b":"e","\u0205":"e","\u0207":"e","\u1eb9":"e","\u1ec7":"e","\u0229":"e","\u1e1d":"e","\u0119":"e","\u1e19":"e","\u1e1b":"e","\u0247":"e","\u025b":"e","\u01dd":"e","\u24d5":"f","\uff46":"f","\u1e1f":"f","\u0192":"f","\ua77c":"f","\u24d6":"g","\uff47":"g","\u01f5":"g","\u011d":"g","\u1e21":"g","\u011f":"g","\u0121":"g","\u01e7":"g","\u0123":"g","\u01e5":"g","\u0260":"g","\ua7a1":"g","\u1d79":"g","\ua77f":"g","\u24d7":"h","\uff48":"h","\u0125":"h","\u1e23":"h","\u1e27":"h","\u021f":"h","\u1e25":"h","\u1e29":"h","\u1e2b":"h","\u1e96":"h","\u0127":"h","\u2c68":"h","\u2c76":"h","\u0265":"h","\u0195":"hv","\u24d8":"i","\uff49":"i","\xec":"i","\xed":"i","\xee":"i","\u0129":"i","\u012b":"i","\u012d":"i","\xef":"i","\u1e2f":"i","\u1ec9":"i","\u01d0":"i","\u0209":"i","\u020b":"i","\u1ecb":"i","\u012f":"i","\u1e2d":"i","\u0268":"i","\u0131":"i","\u24d9":"j","\uff4a":"j","\u0135":"j","\u01f0":"j","\u0249":"j","\u24da":"k","\uff4b":"k","\u1e31":"k","\u01e9":"k","\u1e33":"k","\u0137":"k","\u1e35":"k","\u0199":"k","\u2c6a":"k","\ua741":"k","\ua743":"k","\ua745":"k","\ua7a3":"k","\u24db":"l","\uff4c":"l","\u0140":"l","\u013a":"l","\u013e":"l","\u1e37":"l","\u1e39":"l","\u013c":"l","\u1e3d":"l","\u1e3b":"l","\u017f":"l","\u0142":"l","\u019a":"l","\u026b":"l","\u2c61":"l","\ua749":"l","\ua781":"l","\ua747":"l","\u01c9":"lj","\u24dc":"m","\uff4d":"m","\u1e3f":"m","\u1e41":"m","\u1e43":"m","\u0271":"m","\u026f":"m","\u24dd":"n","\uff4e":"n","\u01f9":"n","\u0144":"n","\xf1":"n","\u1e45":"n","\u0148":"n","\u1e47":"n","\u0146":"n","\u1e4b":"n","\u1e49":"n","\u019e":"n","\u0272":"n","\u0149":"n","\ua791":"n","\ua7a5":"n","\u01cc":"nj","\u24de":"o","\uff4f":"o","\xf2":"o","\xf3":"o","\xf4":"o","\u1ed3":"o","\u1ed1":"o","\u1ed7":"o","\u1ed5":"o","\xf5":"o","\u1e4d":"o","\u022d":"o","\u1e4f":"o","\u014d":"o","\u1e51":"o","\u1e53":"o","\u014f":"o","\u022f":"o","\u0231":"o","\xf6":"o","\u022b":"o","\u1ecf":"o","\u0151":"o","\u01d2":"o","\u020d":"o","\u020f":"o","\u01a1":"o","\u1edd":"o","\u1edb":"o","\u1ee1":"o","\u1edf":"o","\u1ee3":"o","\u1ecd":"o","\u1ed9":"o","\u01eb":"o","\u01ed":"o","\xf8":"o","\u01ff":"o","\u0254":"o","\ua74b":"o","\ua74d":"o","\u0275":"o","\u01a3":"oi","\u0223":"ou","\ua74f":"oo","\u24df":"p","\uff50":"p","\u1e55":"p","\u1e57":"p","\u01a5":"p","\u1d7d":"p","\ua751":"p","\ua753":"p","\ua755":"p","\u24e0":"q","\uff51":"q","\u024b":"q","\ua757":"q","\ua759":"q","\u24e1":"r","\uff52":"r","\u0155":"r","\u1e59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1e5b":"r","\u1e5d":"r","\u0157":"r","\u1e5f":"r","\u024d":"r","\u027d":"r","\ua75b":"r","\ua7a7":"r","\ua783":"r","\u24e2":"s","\uff53":"s","\xdf":"s","\u015b":"s","\u1e65":"s","\u015d":"s","\u1e61":"s","\u0161":"s","\u1e67":"s","\u1e63":"s","\u1e69":"s","\u0219":"s","\u015f":"s","\u023f":"s","\ua7a9":"s","\ua785":"s","\u1e9b":"s","\u24e3":"t","\uff54":"t","\u1e6b":"t","\u1e97":"t","\u0165":"t","\u1e6d":"t","\u021b":"t","\u0163":"t","\u1e71":"t","\u1e6f":"t","\u0167":"t","\u01ad":"t","\u0288":"t","\u2c66":"t","\ua787":"t","\ua729":"tz","\u24e4":"u","\uff55":"u","\xf9":"u","\xfa":"u","\xfb":"u","\u0169":"u","\u1e79":"u","\u016b":"u","\u1e7b":"u","\u016d":"u","\xfc":"u","\u01dc":"u","\u01d8":"u","\u01d6":"u","\u01da":"u","\u1ee7":"u","\u016f":"u","\u0171":"u","\u01d4":"u","\u0215":"u","\u0217":"u","\u01b0":"u","\u1eeb":"u","\u1ee9":"u","\u1eef":"u","\u1eed":"u","\u1ef1":"u","\u1ee5":"u","\u1e73":"u","\u0173":"u","\u1e77":"u","\u1e75":"u","\u0289":"u","\u24e5":"v","\uff56":"v","\u1e7d":"v","\u1e7f":"v","\u028b":"v","\ua75f":"v","\u028c":"v","\ua761":"vy","\u24e6":"w","\uff57":"w","\u1e81":"w","\u1e83":"w","\u0175":"w","\u1e87":"w","\u1e85":"w","\u1e98":"w","\u1e89":"w","\u2c73":"w","\u24e7":"x","\uff58":"x","\u1e8b":"x","\u1e8d":"x","\u24e8":"y","\uff59":"y","\u1ef3":"y","\xfd":"y","\u0177":"y","\u1ef9":"y","\u0233":"y","\u1e8f":"y","\xff":"y","\u1ef7":"y","\u1e99":"y","\u1ef5":"y","\u01b4":"y","\u024f":"y","\u1eff":"y","\u24e9":"z","\uff5a":"z","\u017a":"z","\u1e91":"z","\u017c":"z","\u017e":"z","\u1e93":"z","\u1e95":"z","\u01b6":"z","\u0225":"z","\u0240":"z","\u2c6c":"z","\ua763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038a":"\u0399","\u03aa":"\u0399","\u038c":"\u039f","\u038e":"\u03a5","\u03ab":"\u03a5","\u038f":"\u03a9","\u03ac":"\u03b1","\u03ad":"\u03b5","\u03ae":"\u03b7","\u03af":"\u03b9","\u03ca":"\u03b9","\u0390":"\u03b9","\u03cc":"\u03bf","\u03cd":"\u03c5","\u03cb":"\u03c5","\u03b0":"\u03c5","\u03c9":"\u03c9","\u03c2":"\u03c3"};i=a(document),f=function(){var a=1;return function(){return a++}}(),c=O(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(c){var d,e,g=".select2-results";this.opts=c=this.prepareOpts(c),this.id=c.id,c.element.data("select2")!==b&&null!==c.element.data("select2")&&c.element.data("select2").destroy(),this.container=this.createContainer(),this.liveRegion=a(".select2-hidden-accessible"),0==this.liveRegion.length&&(this.liveRegion=a("<span>",{role:"status","aria-live":"polite"}).addClass("select2-hidden-accessible").appendTo(document.body)),this.containerId="s2id_"+(c.element.attr("id")||"autogen"+f()),this.containerEventName=this.containerId.replace(/([.])/g,"_").replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1"),this.container.attr("id",this.containerId),this.container.attr("title",c.element.attr("title")),this.body=a(document.body),D(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.attr("style",c.element.attr("style")),this.container.css(K(c.containerCss,this.opts.element)),this.container.addClass(K(c.containerCssClass,this.opts.element)),this.elementTabIndex=this.opts.element.attr("tabindex"),this.opts.element.data("select2",this).attr("tabindex","-1").before(this.container).on("click.select2",A),this.container.data("select2",this),this.dropdown=this.container.find(".select2-drop"),D(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(K(c.dropdownCssClass,this.opts.element)),this.dropdown.data("select2",this),this.dropdown.on("click",A),this.results=d=this.container.find(g),this.search=e=this.container.find("input.select2-input"),this.queryCount=0,this.resultsPage=0,this.context=null,this.initContainer(),this.container.on("click",A),v(this.results),this.dropdown.on("mousemove-filtered",g,this.bind(this.highlightUnderEvent)),this.dropdown.on("touchstart touchmove touchend",g,this.bind(function(a){this._touchEvent=!0,this.highlightUnderEvent(a)})),this.dropdown.on("touchmove",g,this.bind(this.touchMoved)),this.dropdown.on("touchstart touchend",g,this.bind(this.clearTouchMoved)),this.dropdown.on("click",this.bind(function(){this._touchEvent&&(this._touchEvent=!1,this.selectHighlighted())})),x(80,this.results),this.dropdown.on("scroll-debounced",g,this.bind(this.loadMoreIfNeeded)),a(this.container).on("change",".select2-input",function(a){a.stopPropagation()}),a(this.dropdown).on("change",".select2-input",function(a){a.stopPropagation()}),a.fn.mousewheel&&d.mousewheel(function(a,b,c,e){var f=d.scrollTop();e>0&&0>=f-e?(d.scrollTop(0),A(a)):0>e&&d.get(0).scrollHeight-d.scrollTop()+e<=d.height()&&(d.scrollTop(d.get(0).scrollHeight-d.height()),A(a))}),u(e),e.on("keyup-change input paste",this.bind(this.updateResults)),e.on("focus",function(){e.addClass("select2-focused")}),e.on("blur",function(){e.removeClass("select2-focused")}),this.dropdown.on("mouseup",g,this.bind(function(b){a(b.target).closest(".select2-result-selectable").length>0&&(this.highlightUnderEvent(b),this.selectHighlighted(b))})),this.dropdown.on("click mouseup mousedown touchstart touchend focusin",function(a){a.stopPropagation()}),this.nextSearchTerm=b,a.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource()),null!==c.maximumInputLength&&this.search.attr("maxlength",c.maximumInputLength);var h=c.element.prop("disabled");h===b&&(h=!1),this.enable(!h);var i=c.element.prop("readonly");i===b&&(i=!1),this.readonly(i),j=j||q(),this.autofocus=c.element.prop("autofocus"),c.element.prop("autofocus",!1),this.autofocus&&this.focus(),this.search.attr("placeholder",c.searchInputPlaceholder)},destroy:function(){var a=this.opts.element,c=a.data("select2"),d=this;this.close(),a.length&&a[0].detachEvent&&d._sync&&a.each(function(){d._sync&&this.detachEvent("onpropertychange",d._sync)}),this.propertyObserver&&(this.propertyObserver.disconnect(),this.propertyObserver=null),this._sync=null,c!==b&&(c.container.remove(),c.liveRegion.remove(),c.dropdown.remove(),a.show().removeData("select2").off(".select2").prop("autofocus",this.autofocus||!1),this.elementTabIndex?a.attr({tabindex:this.elementTabIndex}):a.removeAttr("tabindex"),a.show()),N.call(this,"container","liveRegion","dropdown","results","search")},optionToData:function(a){return a.is("option")?{id:a.prop("value"),text:a.text(),element:a.get(),css:a.attr("class"),disabled:a.prop("disabled"),locked:r(a.attr("locked"),"locked")||r(a.data("locked"),!0)}:a.is("optgroup")?{text:a.attr("label"),children:[],element:a.get(),css:a.attr("class")}:void 0},prepareOpts:function(c){var d,e,g,h,i=this;if(d=c.element,"select"===d.get(0).tagName.toLowerCase()&&(this.select=e=c.element),e&&a.each(["id","multiple","ajax","query","createSearchChoice","initSelection","data","tags"],function(){if(this in c)throw new Error("Option '"+this+"' is not allowed for Select2 when attached to a <select> element.")}),c=a.extend({},{populateResults:function(d,e,g){var h,j=this.opts.id,k=this.liveRegion;h=function(d,e,l){var m,n,o,p,q,r,s,t,u,v;d=c.sortResults(d,e,g);var w=[];for(m=0,n=d.length;n>m;m+=1)o=d[m],q=o.disabled===!0,p=!q&&j(o)!==b,r=o.children&&o.children.length>0,s=a("<li></li>"),s.addClass("select2-results-dept-"+l),s.addClass("select2-result"),s.addClass(p?"select2-result-selectable":"select2-result-unselectable"),q&&s.addClass("select2-disabled"),r&&s.addClass("select2-result-with-children"),s.addClass(i.opts.formatResultCssClass(o)),s.attr("role","presentation"),t=a(document.createElement("div")),t.addClass("select2-result-label"),t.attr("id","select2-result-label-"+f()),t.attr("role","option"),v=c.formatResult(o,t,g,i.opts.escapeMarkup),v!==b&&(t.html(v),s.append(t)),r&&(u=a("<ul></ul>"),u.addClass("select2-result-sub"),h(o.children,u,l+1),s.append(u)),s.data("select2-data",o),w.push(s[0]);e.append(w),k.text(c.formatMatches(d.length))},h(e,d,0)}},a.fn.select2.defaults,c),"function"!=typeof c.id&&(g=c.id,c.id=function(a){return a[g]}),a.isArray(c.element.data("select2Tags"))){if("tags"in c)throw"tags specified as both an attribute 'data-select2-tags' and in options of Select2 "+c.element.attr("id");c.tags=c.element.data("select2Tags")}if(e?(c.query=this.bind(function(a){var f,g,h,c={results:[],more:!1},e=a.term;h=function(b,c){var d;b.is("option")?a.matcher(e,b.text(),b)&&c.push(i.optionToData(b)):b.is("optgroup")&&(d=i.optionToData(b),b.children().each2(function(a,b){h(b,d.children)}),d.children.length>0&&c.push(d))},f=d.children(),this.getPlaceholder()!==b&&f.length>0&&(g=this.getPlaceholderOption(),g&&(f=f.not(g))),f.each2(function(a,b){h(b,c.results)}),a.callback(c)}),c.id=function(a){return a.id}):"query"in c||("ajax"in c?(h=c.element.data("ajax-url"),h&&h.length>0&&(c.ajax.url=h),c.query=G.call(c.element,c.ajax)):"data"in c?c.query=H(c.data):"tags"in c&&(c.query=I(c.tags),c.createSearchChoice===b&&(c.createSearchChoice=function(b){return{id:a.trim(b),text:a.trim(b)}}),c.initSelection===b&&(c.initSelection=function(b,d){var e=[];a(s(b.val(),c.separator,c.transformVal)).each(function(){var b={id:this,text:this},d=c.tags;a.isFunction(d)&&(d=d()),a(d).each(function(){return r(this.id,b.id)?(b=this,!1):void 0}),e.push(b)}),d(e)}))),"function"!=typeof c.query)throw"query function not defined for Select2 "+c.element.attr("id");if("top"===c.createSearchChoicePosition)c.createSearchChoicePosition=function(a,b){a.unshift(b)};else if("bottom"===c.createSearchChoicePosition)c.createSearchChoicePosition=function(a,b){a.push(b)};else if("function"!=typeof c.createSearchChoicePosition)throw"invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";return c},monitorSource:function(){var d,c=this.opts.element,e=this;c.on("change.select2",this.bind(function(){this.opts.element.data("select2-change-triggered")!==!0&&this.initSelection()})),this._sync=this.bind(function(){var a=c.prop("disabled");a===b&&(a=!1),this.enable(!a);var d=c.prop("readonly");d===b&&(d=!1),this.readonly(d),this.container&&(D(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.addClass(K(this.opts.containerCssClass,this.opts.element))),this.dropdown&&(D(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(K(this.opts.dropdownCssClass,this.opts.element)))}),c.length&&c[0].attachEvent&&c.each(function(){this.attachEvent("onpropertychange",e._sync)}),d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver,d!==b&&(this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),this.propertyObserver=new d(function(b){a.each(b,e._sync)}),this.propertyObserver.observe(c.get(0),{attributes:!0,subtree:!1}))},triggerSelect:function(b){var c=a.Event("select2-selecting",{val:this.id(b),object:b,choice:b});return this.opts.element.trigger(c),!c.isDefaultPrevented()},triggerChange:function(b){b=b||{},b=a.extend({},b,{type:"change",val:this.val()}),this.opts.element.data("select2-change-triggered",!0),this.opts.element.trigger(b),this.opts.element.data("select2-change-triggered",!1),this.opts.element.click(),this.opts.blurOnChange&&this.opts.element.blur()},isInterfaceEnabled:function(){return this.enabledInterface===!0},enableInterface:function(){var a=this._enabled&&!this._readonly,b=!a;return a===this.enabledInterface?!1:(this.container.toggleClass("select2-container-disabled",b),this.close(),this.enabledInterface=a,!0)},enable:function(a){a===b&&(a=!0),this._enabled!==a&&(this._enabled=a,this.opts.element.prop("disabled",!a),this.enableInterface())},disable:function(){this.enable(!1)},readonly:function(a){a===b&&(a=!1),this._readonly!==a&&(this._readonly=a,this.opts.element.prop("readonly",a),this.enableInterface())},opened:function(){return this.container?this.container.hasClass("select2-dropdown-open"):!1},positionDropdown:function(){var v,w,x,y,z,b=this.dropdown,c=this.container,d=c.offset(),e=c.outerHeight(!1),f=c.outerWidth(!1),g=b.outerHeight(!1),h=a(window),i=h.width(),k=h.height(),l=h.scrollLeft()+i,m=h.scrollTop()+k,n=d.top+e,o=d.left,p=m>=n+g,q=d.top-g>=h.scrollTop(),r=b.outerWidth(!1),s=function(){return l>=o+r},t=function(){return d.left+l+c.outerWidth(!1)>r},u=b.hasClass("select2-drop-above");u?(w=!0,!q&&p&&(x=!0,w=!1)):(w=!1,!p&&q&&(x=!0,w=!0)),x&&(b.hide(),d=this.container.offset(),e=this.container.outerHeight(!1),f=this.container.outerWidth(!1),g=b.outerHeight(!1),l=h.scrollLeft()+i,m=h.scrollTop()+k,n=d.top+e,o=d.left,r=b.outerWidth(!1),b.show(),this.focusSearch()),this.opts.dropdownAutoWidth?(z=a(".select2-results",b)[0],b.addClass("select2-drop-auto-width"),b.css("width",""),r=b.outerWidth(!1)+(z.scrollHeight===z.clientHeight?0:j.width),r>f?f=r:r=f,g=b.outerHeight(!1)):this.container.removeClass("select2-drop-auto-width"),"static"!==this.body.css("position")&&(v=this.body.offset(),n-=v.top,o-=v.left),!s()&&t()&&(o=d.left+this.container.outerWidth(!1)-r),y={left:o,width:f},w?(y.top=d.top-g,y.bottom="auto",this.container.addClass("select2-drop-above"),b.addClass("select2-drop-above")):(y.top=n,y.bottom="auto",this.container.removeClass("select2-drop-above"),b.removeClass("select2-drop-above")),y=a.extend(y,K(this.opts.dropdownCss,this.opts.element)),b.css(y)},shouldOpen:function(){var b;return this.opened()?!1:this._enabled===!1||this._readonly===!0?!1:(b=a.Event("select2-opening"),this.opts.element.trigger(b),!b.isDefaultPrevented())},clearDropdownAlignmentPreference:function(){this.container.removeClass("select2-drop-above"),this.dropdown.removeClass("select2-drop-above")},open:function(){return this.shouldOpen()?(this.opening(),i.on("mousemove.select2Event",function(a){h.x=a.pageX,h.y=a.pageY}),!0):!1},opening:function(){var f,b=this.containerEventName,c="scroll."+b,d="resize."+b,e="orientationchange."+b;this.container.addClass("select2-dropdown-open").addClass("select2-container-active"),this.clearDropdownAlignmentPreference(),this.dropdown[0]!==this.body.children().last()[0]&&this.dropdown.detach().appendTo(this.body),f=a("#select2-drop-mask"),0===f.length&&(f=a(document.createElement("div")),f.attr("id","select2-drop-mask").attr("class","select2-drop-mask"),f.hide(),f.appendTo(this.body),f.on("mousedown touchstart click",function(b){n(f);var d,c=a("#select2-drop");c.length>0&&(d=c.data("select2"),d.opts.selectOnBlur&&d.selectHighlighted({noFocus:!0}),d.close(),b.preventDefault(),b.stopPropagation())})),this.dropdown.prev()[0]!==f[0]&&this.dropdown.before(f),a("#select2-drop").removeAttr("id"),this.dropdown.attr("id","select2-drop"),f.show(),this.positionDropdown(),this.dropdown.show(),this.positionDropdown(),this.dropdown.addClass("select2-drop-active");var g=this;this.container.parents().add(window).each(function(){a(this).on(d+" "+c+" "+e,function(){g.opened()&&g.positionDropdown()})})},close:function(){if(this.opened()){var b=this.containerEventName,c="scroll."+b,d="resize."+b,e="orientationchange."+b;this.container.parents().add(window).each(function(){a(this).off(c).off(d).off(e)}),this.clearDropdownAlignmentPreference(),a("#select2-drop-mask").hide(),this.dropdown.removeAttr("id"),this.dropdown.hide(),this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"),this.results.empty(),i.off("mousemove.select2Event"),this.clearSearch(),this.search.removeClass("select2-active"),this.opts.element.trigger(a.Event("select2-close"))}},externalSearch:function(a){this.open(),this.search.val(a),this.updateResults(!1)},clearSearch:function(){},getMaximumSelectionSize:function(){return K(this.opts.maximumSelectionSize,this.opts.element)},ensureHighlightVisible:function(){var c,d,e,f,g,h,i,j,b=this.results;if(d=this.highlight(),!(0>d)){if(0==d)return b.scrollTop(0),void 0;c=this.findHighlightableChoices().find(".select2-result-label"),e=a(c[d]),j=(e.offset()||{}).top||0,f=j+e.outerHeight(!0),d===c.length-1&&(i=b.find("li.select2-more-results"),i.length>0&&(f=i.offset().top+i.outerHeight(!0))),g=b.offset().top+b.outerHeight(!1),f>g&&b.scrollTop(b.scrollTop()+(f-g)),h=j-b.offset().top,0>h&&"none"!=e.css("display")&&b.scrollTop(b.scrollTop()+h)}},findHighlightableChoices:function(){return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)")},moveHighlight:function(b){for(var c=this.findHighlightableChoices(),d=this.highlight();d>-1&&d<c.length;){d+=b;
22
+ var e=a(c[d]);if(e.hasClass("select2-result-selectable")&&!e.hasClass("select2-disabled")&&!e.hasClass("select2-selected")){this.highlight(d);break}}},highlight:function(b){var d,e,c=this.findHighlightableChoices();return 0===arguments.length?p(c.filter(".select2-highlighted")[0],c.get()):(b>=c.length&&(b=c.length-1),0>b&&(b=0),this.removeHighlight(),d=a(c[b]),d.addClass("select2-highlighted"),this.search.attr("aria-activedescendant",d.find(".select2-result-label").attr("id")),this.ensureHighlightVisible(),this.liveRegion.text(d.text()),e=d.data("select2-data"),e&&this.opts.element.trigger({type:"select2-highlight",val:this.id(e),choice:e}),void 0)},removeHighlight:function(){this.results.find(".select2-highlighted").removeClass("select2-highlighted")},touchMoved:function(){this._touchMoved=!0},clearTouchMoved:function(){this._touchMoved=!1},countSelectableResults:function(){return this.findHighlightableChoices().length},highlightUnderEvent:function(b){var c=a(b.target).closest(".select2-result-selectable");if(c.length>0&&!c.is(".select2-highlighted")){var d=this.findHighlightableChoices();this.highlight(d.index(c))}else 0==c.length&&this.removeHighlight()},loadMoreIfNeeded:function(){var c,a=this.results,b=a.find("li.select2-more-results"),d=this.resultsPage+1,e=this,f=this.search.val(),g=this.context;0!==b.length&&(c=b.offset().top-a.offset().top-a.height(),c<=this.opts.loadMorePadding&&(b.addClass("select2-active"),this.opts.query({element:this.opts.element,term:f,page:d,context:g,matcher:this.opts.matcher,callback:this.bind(function(c){e.opened()&&(e.opts.populateResults.call(this,a,c.results,{term:f,page:d,context:g}),e.postprocessResults(c,!1,!1),c.more===!0?(b.detach().appendTo(a).html(e.opts.escapeMarkup(K(e.opts.formatLoadMore,e.opts.element,d+1))),window.setTimeout(function(){e.loadMoreIfNeeded()},10)):b.remove(),e.positionDropdown(),e.resultsPage=d,e.context=c.context,this.opts.element.trigger({type:"select2-loaded",items:c}))})})))},tokenize:function(){},updateResults:function(c){function m(){d.removeClass("select2-active"),h.positionDropdown(),e.find(".select2-no-results,.select2-selection-limit,.select2-searching").length?h.liveRegion.text(e.text()):h.liveRegion.text(h.opts.formatMatches(e.find('.select2-result-selectable:not(".select2-selected")').length))}function n(a){e.html(a),m()}var g,i,l,d=this.search,e=this.results,f=this.opts,h=this,j=d.val(),k=a.data(this.container,"select2-last-term");if((c===!0||!k||!r(j,k))&&(a.data(this.container,"select2-last-term",j),c===!0||this.showSearchInput!==!1&&this.opened())){l=++this.queryCount;var o=this.getMaximumSelectionSize();if(o>=1&&(g=this.data(),a.isArray(g)&&g.length>=o&&J(f.formatSelectionTooBig,"formatSelectionTooBig")))return n("<li class='select2-selection-limit'>"+K(f.formatSelectionTooBig,f.element,o)+"</li>"),void 0;if(d.val().length<f.minimumInputLength)return J(f.formatInputTooShort,"formatInputTooShort")?n("<li class='select2-no-results'>"+K(f.formatInputTooShort,f.element,d.val(),f.minimumInputLength)+"</li>"):n(""),c&&this.showSearch&&this.showSearch(!0),void 0;if(f.maximumInputLength&&d.val().length>f.maximumInputLength)return J(f.formatInputTooLong,"formatInputTooLong")?n("<li class='select2-no-results'>"+K(f.formatInputTooLong,f.element,d.val(),f.maximumInputLength)+"</li>"):n(""),void 0;f.formatSearching&&0===this.findHighlightableChoices().length&&n("<li class='select2-searching'>"+K(f.formatSearching,f.element)+"</li>"),d.addClass("select2-active"),this.removeHighlight(),i=this.tokenize(),i!=b&&null!=i&&d.val(i),this.resultsPage=1,f.query({element:f.element,term:d.val(),page:this.resultsPage,context:null,matcher:f.matcher,callback:this.bind(function(g){var i;if(l==this.queryCount){if(!this.opened())return this.search.removeClass("select2-active"),void 0;if(g.hasError!==b&&J(f.formatAjaxError,"formatAjaxError"))return n("<li class='select2-ajax-error'>"+K(f.formatAjaxError,f.element,g.jqXHR,g.textStatus,g.errorThrown)+"</li>"),void 0;if(this.context=g.context===b?null:g.context,this.opts.createSearchChoice&&""!==d.val()&&(i=this.opts.createSearchChoice.call(h,d.val(),g.results),i!==b&&null!==i&&h.id(i)!==b&&null!==h.id(i)&&0===a(g.results).filter(function(){return r(h.id(this),h.id(i))}).length&&this.opts.createSearchChoicePosition(g.results,i)),0===g.results.length&&J(f.formatNoMatches,"formatNoMatches"))return n("<li class='select2-no-results'>"+K(f.formatNoMatches,f.element,d.val())+"</li>"),void 0;e.empty(),h.opts.populateResults.call(this,e,g.results,{term:d.val(),page:this.resultsPage,context:null}),g.more===!0&&J(f.formatLoadMore,"formatLoadMore")&&(e.append("<li class='select2-more-results'>"+f.escapeMarkup(K(f.formatLoadMore,f.element,this.resultsPage))+"</li>"),window.setTimeout(function(){h.loadMoreIfNeeded()},10)),this.postprocessResults(g,c),m(),this.opts.element.trigger({type:"select2-loaded",items:g})}})})}},cancel:function(){this.close()},blur:function(){this.opts.selectOnBlur&&this.selectHighlighted({noFocus:!0}),this.close(),this.container.removeClass("select2-container-active"),this.search[0]===document.activeElement&&this.search.blur(),this.clearSearch(),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus")},focusSearch:function(){y(this.search)},selectHighlighted:function(a){if(this._touchMoved)return this.clearTouchMoved(),void 0;var b=this.highlight(),c=this.results.find(".select2-highlighted"),d=c.closest(".select2-result").data("select2-data");d?(this.highlight(b),this.onSelect(d,a)):a&&a.noFocus&&this.close()},getPlaceholder:function(){var a;return this.opts.element.attr("placeholder")||this.opts.element.attr("data-placeholder")||this.opts.element.data("placeholder")||this.opts.placeholder||((a=this.getPlaceholderOption())!==b?a.text():b)},getPlaceholderOption:function(){if(this.select){var c=this.select.children("option").first();if(this.opts.placeholderOption!==b)return"first"===this.opts.placeholderOption&&c||"function"==typeof this.opts.placeholderOption&&this.opts.placeholderOption(this.select);if(""===a.trim(c.text())&&""===c.val())return c}},initContainerWidth:function(){function c(){var c,d,e,f,g,h;if("off"===this.opts.width)return null;if("element"===this.opts.width)return 0===this.opts.element.outerWidth(!1)?"auto":this.opts.element.outerWidth(!1)+"px";if("copy"===this.opts.width||"resolve"===this.opts.width){if(c=this.opts.element.attr("style"),c!==b)for(d=c.split(";"),f=0,g=d.length;g>f;f+=1)if(h=d[f].replace(/\s/g,""),e=h.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i),null!==e&&e.length>=1)return e[1];return"resolve"===this.opts.width?(c=this.opts.element.css("width"),c.indexOf("%")>0?c:0===this.opts.element.outerWidth(!1)?"auto":this.opts.element.outerWidth(!1)+"px"):null}return a.isFunction(this.opts.width)?this.opts.width():this.opts.width}var d=c.call(this);null!==d&&this.container.css("width",d)}}),d=O(c,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container"}).html(["<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>"," <span class='select2-chosen'>&#160;</span><abbr class='select2-search-choice-close'></abbr>"," <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>","</a>","<label for='' class='select2-offscreen'></label>","<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />","<div class='select2-drop select2-display-none'>"," <div class='select2-search'>"," <label for='' class='select2-offscreen'></label>"," <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'"," aria-autocomplete='list' />"," </div>"," <ul class='select2-results' role='listbox'>"," </ul>","</div>"].join(""));return b},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.focusser.prop("disabled",!this.isInterfaceEnabled())},opening:function(){var c,d,e;this.opts.minimumResultsForSearch>=0&&this.showSearch(!0),this.parent.opening.apply(this,arguments),this.showSearchInput!==!1&&this.search.val(this.focusser.val()),this.opts.shouldFocusInput(this)&&(this.search.focus(),c=this.search.get(0),c.createTextRange?(d=c.createTextRange(),d.collapse(!1),d.select()):c.setSelectionRange&&(e=this.search.val().length,c.setSelectionRange(e,e))),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.focusser.prop("disabled",!0).val(""),this.updateResults(!0),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus())},focus:function(){this.opened()?this.close():(this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus())},isFocused:function(){return this.container.hasClass("select2-container-active")},cancel:function(){this.parent.cancel.apply(this,arguments),this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus()},destroy:function(){a("label[for='"+this.focusser.attr("id")+"']").attr("for",this.opts.element.attr("id")),this.parent.destroy.apply(this,arguments),N.call(this,"selection","focusser")},initContainer:function(){var b,g,c=this.container,d=this.dropdown,e=f();this.opts.minimumResultsForSearch<0?this.showSearch(!1):this.showSearch(!0),this.selection=b=c.find(".select2-choice"),this.focusser=c.find(".select2-focusser"),b.find(".select2-chosen").attr("id","select2-chosen-"+e),this.focusser.attr("aria-labelledby","select2-chosen-"+e),this.results.attr("id","select2-results-"+e),this.search.attr("aria-owns","select2-results-"+e),this.focusser.attr("id","s2id_autogen"+e),g=a("label[for='"+this.opts.element.attr("id")+"']"),this.opts.element.focus(this.bind(function(){this.focus()})),this.focusser.prev().text(g.text()).attr("for",this.focusser.attr("id"));var h=this.opts.element.attr("title");this.opts.element.attr("title",h||g.text()),this.focusser.attr("tabindex",this.elementTabIndex),this.search.attr("id",this.focusser.attr("id")+"_search"),this.search.prev().text(a("label[for='"+this.focusser.attr("id")+"']").text()).attr("for",this.search.attr("id")),this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&229!=a.keyCode){if(a.which===k.PAGE_UP||a.which===k.PAGE_DOWN)return A(a),void 0;switch(a.which){case k.UP:case k.DOWN:return this.moveHighlight(a.which===k.UP?-1:1),A(a),void 0;case k.ENTER:return this.selectHighlighted(),A(a),void 0;case k.TAB:return this.selectHighlighted({noFocus:!0}),void 0;case k.ESC:return this.cancel(a),A(a),void 0}}})),this.search.on("blur",this.bind(function(){document.activeElement===this.body.get(0)&&window.setTimeout(this.bind(function(){this.opened()&&this.search.focus()}),0)})),this.focusser.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&a.which!==k.TAB&&!k.isControl(a)&&!k.isFunctionKey(a)&&a.which!==k.ESC){if(this.opts.openOnEnter===!1&&a.which===k.ENTER)return A(a),void 0;if(a.which==k.DOWN||a.which==k.UP||a.which==k.ENTER&&this.opts.openOnEnter){if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return;return this.open(),A(a),void 0}return a.which==k.DELETE||a.which==k.BACKSPACE?(this.opts.allowClear&&this.clear(),A(a),void 0):void 0}})),u(this.focusser),this.focusser.on("keyup-change input",this.bind(function(a){if(this.opts.minimumResultsForSearch>=0){if(a.stopPropagation(),this.opened())return;this.open()}})),b.on("mousedown touchstart","abbr",this.bind(function(a){this.isInterfaceEnabled()&&(this.clear(),B(a),this.close(),this.selection&&this.selection.focus())})),b.on("mousedown touchstart",this.bind(function(c){n(b),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.opened()?this.close():this.isInterfaceEnabled()&&this.open(),A(c)})),d.on("mousedown touchstart",this.bind(function(){this.opts.shouldFocusInput(this)&&this.search.focus()})),b.on("focus",this.bind(function(a){A(a)})),this.focusser.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})).on("blur",this.bind(function(){this.opened()||(this.container.removeClass("select2-container-active"),this.opts.element.trigger(a.Event("select2-blur")))})),this.search.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})),this.initContainerWidth(),this.opts.element.hide(),this.setPlaceholder()},clear:function(b){var c=this.selection.data("select2-data");if(c){var d=a.Event("select2-clearing");if(this.opts.element.trigger(d),d.isDefaultPrevented())return;var e=this.getPlaceholderOption();this.opts.element.val(e?e.val():""),this.selection.find(".select2-chosen").empty(),this.selection.removeData("select2-data"),this.setPlaceholder(),b!==!1&&(this.opts.element.trigger({type:"select2-removed",val:this.id(c),choice:c}),this.triggerChange({removed:c}))}},initSelection:function(){if(this.isPlaceholderOptionSelected())this.updateSelection(null),this.close(),this.setPlaceholder();else{var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.setPlaceholder(),c.nextSearchTerm=c.opts.nextSearchTerm(a,c.search.val()))})}},isPlaceholderOptionSelected:function(){var a;return this.getPlaceholder()===b?!1:(a=this.getPlaceholderOption())!==b&&a.prop("selected")||""===this.opts.element.val()||this.opts.element.val()===b||null===this.opts.element.val()},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=a.find("option").filter(function(){return this.selected&&!this.disabled});b(c.optionToData(d))}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=c.val(),f=null;b.query({matcher:function(a,c,d){var g=r(e,b.id(d));return g&&(f=d),g},callback:a.isFunction(d)?function(){d(f)}:a.noop})}),b},getPlaceholder:function(){return this.select&&this.getPlaceholderOption()===b?b:this.parent.getPlaceholder.apply(this,arguments)},setPlaceholder:function(){var a=this.getPlaceholder();if(this.isPlaceholderOptionSelected()&&a!==b){if(this.select&&this.getPlaceholderOption()===b)return;this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.container.removeClass("select2-allowclear")}},postprocessResults:function(a,b,c){var d=0,e=this;if(this.findHighlightableChoices().each2(function(a,b){return r(e.id(b.data("select2-data")),e.opts.element.val())?(d=a,!1):void 0}),c!==!1&&(b===!0&&d>=0?this.highlight(d):this.highlight(0)),b===!0){var g=this.opts.minimumResultsForSearch;g>=0&&this.showSearch(L(a.results)>=g)}},showSearch:function(b){this.showSearchInput!==b&&(this.showSearchInput=b,this.dropdown.find(".select2-search").toggleClass("select2-search-hidden",!b),this.dropdown.find(".select2-search").toggleClass("select2-offscreen",!b),a(this.dropdown,this.container).toggleClass("select2-with-searchbox",b))},onSelect:function(a,b){if(this.triggerSelect(a)){var c=this.opts.element.val(),d=this.data();this.opts.element.val(this.id(a)),this.updateSelection(a),this.opts.element.trigger({type:"select2-selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.close(),b&&b.noFocus||!this.opts.shouldFocusInput(this)||this.focusser.focus(),r(c,this.id(a))||this.triggerChange({added:a,removed:d})}},updateSelection:function(a){var d,e,c=this.selection.find(".select2-chosen");this.selection.data("select2-data",a),c.empty(),null!==a&&(d=this.opts.formatSelection(a,c,this.opts.escapeMarkup)),d!==b&&c.append(d),e=this.opts.formatSelectionCssClass(a,c),e!==b&&c.addClass(e),this.selection.removeClass("select2-default"),this.opts.allowClear&&this.getPlaceholder()!==b&&this.container.addClass("select2-allowclear")},val:function(){var a,c=!1,d=null,e=this,f=this.data();if(0===arguments.length)return this.opts.element.val();if(a=arguments[0],arguments.length>1&&(c=arguments[1]),this.select)this.select.val(a).find("option").filter(function(){return this.selected}).each2(function(a,b){return d=e.optionToData(b),!1}),this.updateSelection(d),this.setPlaceholder(),c&&this.triggerChange({added:d,removed:f});else{if(!a&&0!==a)return this.clear(c),void 0;if(this.opts.initSelection===b)throw new Error("cannot call val() if initSelection() is not defined");this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){e.opts.element.val(a?e.id(a):""),e.updateSelection(a),e.setPlaceholder(),c&&e.triggerChange({added:a,removed:f})})}},clearSearch:function(){this.search.val(""),this.focusser.val("")},data:function(a){var c,d=!1;return 0===arguments.length?(c=this.selection.data("select2-data"),c==b&&(c=null),c):(arguments.length>1&&(d=arguments[1]),a?(c=this.data(),this.opts.element.val(a?this.id(a):""),this.updateSelection(a),d&&this.triggerChange({added:a,removed:c})):this.clear(d),void 0)}}),e=O(c,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container select2-container-multi"}).html(["<ul class='select2-choices'>"," <li class='select2-search-field'>"," <label for='' class='select2-offscreen'></label>"," <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>"," </li>","</ul>","<div class='select2-drop select2-drop-multi select2-display-none'>"," <ul class='select2-results'>"," </ul>","</div>"].join(""));return b},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=[];a.find("option").filter(function(){return this.selected&&!this.disabled}).each2(function(a,b){d.push(c.optionToData(b))}),b(d)}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=s(c.val(),b.separator,b.transformVal),f=[];b.query({matcher:function(c,d,g){var h=a.grep(e,function(a){return r(a,b.id(g))}).length;return h&&f.push(g),h},callback:a.isFunction(d)?function(){for(var a=[],c=0;c<e.length;c++)for(var g=e[c],h=0;h<f.length;h++){var i=f[h];if(r(g,b.id(i))){a.push(i),f.splice(h,1);break}}d(a)}:a.noop})}),b},selectChoice:function(a){var b=this.container.find(".select2-search-choice-focus");b.length&&a&&a[0]==b[0]||(b.length&&this.opts.element.trigger("choice-deselected",b),b.removeClass("select2-search-choice-focus"),a&&a.length&&(this.close(),a.addClass("select2-search-choice-focus"),this.opts.element.trigger("choice-selected",a)))},destroy:function(){a("label[for='"+this.search.attr("id")+"']").attr("for",this.opts.element.attr("id")),this.parent.destroy.apply(this,arguments),N.call(this,"searchContainer","selection")},initContainer:function(){var c,b=".select2-choices";this.searchContainer=this.container.find(".select2-search-field"),this.selection=c=this.container.find(b);var d=this;this.selection.on("click",".select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)",function(){d.search[0].focus(),d.selectChoice(a(this))}),this.search.attr("id","s2id_autogen"+f()),this.search.prev().text(a("label[for='"+this.opts.element.attr("id")+"']").text()).attr("for",this.search.attr("id")),this.opts.element.focus(this.bind(function(){this.focus()})),this.search.on("input paste",this.bind(function(){this.search.attr("placeholder")&&0==this.search.val().length||this.isInterfaceEnabled()&&(this.opened()||this.open())})),this.search.attr("tabindex",this.elementTabIndex),this.keydowns=0,this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){++this.keydowns;var b=c.find(".select2-search-choice-focus"),d=b.prev(".select2-search-choice:not(.select2-locked)"),e=b.next(".select2-search-choice:not(.select2-locked)"),f=z(this.search);if(b.length&&(a.which==k.LEFT||a.which==k.RIGHT||a.which==k.BACKSPACE||a.which==k.DELETE||a.which==k.ENTER)){var g=b;return a.which==k.LEFT&&d.length?g=d:a.which==k.RIGHT?g=e.length?e:null:a.which===k.BACKSPACE?this.unselect(b.first())&&(this.search.width(10),g=d.length?d:e):a.which==k.DELETE?this.unselect(b.first())&&(this.search.width(10),g=e.length?e:null):a.which==k.ENTER&&(g=null),this.selectChoice(g),A(a),g&&g.length||this.open(),void 0}if((a.which===k.BACKSPACE&&1==this.keydowns||a.which==k.LEFT)&&0==f.offset&&!f.length)return this.selectChoice(c.find(".select2-search-choice:not(.select2-locked)").last()),A(a),void 0;if(this.selectChoice(null),this.opened())switch(a.which){case k.UP:case k.DOWN:return this.moveHighlight(a.which===k.UP?-1:1),A(a),void 0;case k.ENTER:return this.selectHighlighted(),A(a),void 0;case k.TAB:return this.selectHighlighted({noFocus:!0}),this.close(),void 0;case k.ESC:return this.cancel(a),A(a),void 0}if(a.which!==k.TAB&&!k.isControl(a)&&!k.isFunctionKey(a)&&a.which!==k.BACKSPACE&&a.which!==k.ESC){if(a.which===k.ENTER){if(this.opts.openOnEnter===!1)return;if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return}this.open(),(a.which===k.PAGE_UP||a.which===k.PAGE_DOWN)&&A(a),a.which===k.ENTER&&A(a)}}})),this.search.on("keyup",this.bind(function(){this.keydowns=0,this.resizeSearch()})),this.search.on("blur",this.bind(function(b){this.container.removeClass("select2-container-active"),this.search.removeClass("select2-focused"),this.selectChoice(null),this.opened()||this.clearSearch(),b.stopImmediatePropagation(),this.opts.element.trigger(a.Event("select2-blur"))})),this.container.on("click",b,this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").length>0||(this.selectChoice(null),this.clearPlaceholder(),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.open(),this.focusSearch(),b.preventDefault()))})),this.container.on("focus",b,this.bind(function(){this.isInterfaceEnabled()&&(this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())})),this.initContainerWidth(),this.opts.element.hide(),this.clearSearch()},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.search.prop("disabled",!this.isInterfaceEnabled())},initSelection:function(){if(""===this.opts.element.val()&&""===this.opts.element.text()&&(this.updateSelection([]),this.close(),this.clearSearch()),this.select||""!==this.opts.element.val()){var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.clearSearch())})}},clearSearch:function(){var a=this.getPlaceholder(),c=this.getMaxSearchWidth();a!==b&&0===this.getVal().length&&this.search.hasClass("select2-focused")===!1?(this.search.val(a).addClass("select2-default"),this.search.width(c>0?c:this.container.css("width"))):this.search.val("").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")&&this.search.val("").removeClass("select2-default")},opening:function(){this.clearPlaceholder(),this.resizeSearch(),this.parent.opening.apply(this,arguments),this.focusSearch(),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.updateResults(!0),this.opts.shouldFocusInput(this)&&this.search.focus(),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&this.parent.close.apply(this,arguments)},focus:function(){this.close(),this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(b){var c=[],d=[],e=this;a(b).each(function(){p(e.id(this),c)<0&&(c.push(e.id(this)),d.push(this))}),b=d,this.selection.find(".select2-search-choice").remove(),a(b).each(function(){e.addSelectedChoice(this)}),e.postprocessResults()},tokenize:function(){var a=this.search.val();a=this.opts.tokenizer.call(this,a,this.data(),this.bind(this.onSelect),this.opts),null!=a&&a!=b&&(this.search.val(a),a.length>0&&this.open())},onSelect:function(a,c){this.triggerSelect(a)&&""!==a.text&&(this.addSelectedChoice(a),this.opts.element.trigger({type:"selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.clearSearch(),this.updateResults(),(this.select||!this.opts.closeOnSelect)&&this.postprocessResults(a,!1,this.opts.closeOnSelect===!0),this.opts.closeOnSelect?(this.close(),this.search.width(10)):this.countSelectableResults()>0?(this.search.width(10),this.resizeSearch(),this.getMaximumSelectionSize()>0&&this.val().length>=this.getMaximumSelectionSize()?this.updateResults(!0):this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.updateResults(),this.search.select()),this.positionDropdown()):(this.close(),this.search.width(10)),this.triggerChange({added:a}),c&&c.noFocus||this.focusSearch())},cancel:function(){this.close(),this.focusSearch()},addSelectedChoice:function(c){var j,k,d=!c.locked,e=a("<li class='select2-search-choice'> <div></div> <a href='#' class='select2-search-choice-close' tabindex='-1'></a></li>"),f=a("<li class='select2-search-choice select2-locked'><div></div></li>"),g=d?e:f,h=this.id(c),i=this.getVal();j=this.opts.formatSelection(c,g.find("div"),this.opts.escapeMarkup),j!=b&&g.find("div").replaceWith(a("<div></div>").html(j)),k=this.opts.formatSelectionCssClass(c,g.find("div")),k!=b&&g.addClass(k),d&&g.find(".select2-search-choice-close").on("mousedown",A).on("click dblclick",this.bind(function(b){this.isInterfaceEnabled()&&(this.unselect(a(b.target)),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"),A(b),this.close(),this.focusSearch())})).on("focus",this.bind(function(){this.isInterfaceEnabled()&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))})),g.data("select2-data",c),g.insertBefore(this.searchContainer),i.push(h),this.setVal(i)},unselect:function(b){var d,e,c=this.getVal();if(b=b.closest(".select2-search-choice"),0===b.length)throw"Invalid argument: "+b+". Must be .select2-search-choice";if(d=b.data("select2-data")){var f=a.Event("select2-removing");if(f.val=this.id(d),f.choice=d,this.opts.element.trigger(f),f.isDefaultPrevented())return!1;for(;(e=p(this.id(d),c))>=0;)c.splice(e,1),this.setVal(c),this.select&&this.postprocessResults();return b.remove(),this.opts.element.trigger({type:"select2-removed",val:this.id(d),choice:d}),this.triggerChange({removed:d}),!0}},postprocessResults:function(a,b,c){var d=this.getVal(),e=this.results.find(".select2-result"),f=this.results.find(".select2-result-with-children"),g=this;e.each2(function(a,b){var c=g.id(b.data("select2-data"));p(c,d)>=0&&(b.addClass("select2-selected"),b.find(".select2-result-selectable").addClass("select2-selected"))}),f.each2(function(a,b){b.is(".select2-result-selectable")||0!==b.find(".select2-result-selectable:not(.select2-selected)").length||b.addClass("select2-selected")}),-1==this.highlight()&&c!==!1&&this.opts.closeOnSelect===!0&&g.highlight(0),!this.opts.createSearchChoice&&!e.filter(".select2-result:not(.select2-selected)").length>0&&(!a||a&&!a.more&&0===this.results.find(".select2-no-results").length)&&J(g.opts.formatNoMatches,"formatNoMatches")&&this.results.append("<li class='select2-no-results'>"+K(g.opts.formatNoMatches,g.opts.element,g.search.val())+"</li>")},getMaxSearchWidth:function(){return this.selection.width()-t(this.search)},resizeSearch:function(){var a,b,c,d,e,f=t(this.search);a=C(this.search)+10,b=this.search.offset().left,c=this.selection.width(),d=this.selection.offset().left,e=c-(b-d)-f,a>e&&(e=c-f),40>e&&(e=c-f),0>=e&&(e=a),this.search.width(Math.floor(e))},getVal:function(){var a;return this.select?(a=this.select.val(),null===a?[]:a):(a=this.opts.element.val(),s(a,this.opts.separator,this.opts.transformVal))},setVal:function(b){var c;this.select?this.select.val(b):(c=[],a(b).each(function(){p(this,c)<0&&c.push(this)}),this.opts.element.val(0===c.length?"":c.join(this.opts.separator)))},buildChangeDetails:function(a,b){for(var b=b.slice(0),a=a.slice(0),c=0;c<b.length;c++)for(var d=0;d<a.length;d++)r(this.opts.id(b[c]),this.opts.id(a[d]))&&(b.splice(c,1),c>0&&c--,a.splice(d,1),d--);return{added:b,removed:a}},val:function(c,d){var e,f=this;if(0===arguments.length)return this.getVal();if(e=this.data(),e.length||(e=[]),!c&&0!==c)return this.opts.element.val(""),this.updateSelection([]),this.clearSearch(),d&&this.triggerChange({added:this.data(),removed:e}),void 0;if(this.setVal(c),this.select)this.opts.initSelection(this.select,this.bind(this.updateSelection)),d&&this.triggerChange(this.buildChangeDetails(e,this.data()));else{if(this.opts.initSelection===b)throw new Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,function(b){var c=a.map(b,f.id);f.setVal(c),f.updateSelection(b),f.clearSearch(),d&&f.triggerChange(f.buildChangeDetails(e,f.data()))})}this.clearSearch()},onSortStart:function(){if(this.select)throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");this.search.width(0),this.searchContainer.hide()},onSortEnd:function(){var b=[],c=this;this.searchContainer.show(),this.searchContainer.appendTo(this.searchContainer.parent()),this.resizeSearch(),this.selection.find(".select2-search-choice").each(function(){b.push(c.opts.id(a(this).data("select2-data")))}),this.setVal(b),this.triggerChange()},data:function(b,c){var e,f,d=this;return 0===arguments.length?this.selection.children(".select2-search-choice").map(function(){return a(this).data("select2-data")}).get():(f=this.data(),b||(b=[]),e=a.map(b,function(a){return d.opts.id(a)}),this.setVal(e),this.updateSelection(b),this.clearSearch(),c&&this.triggerChange(this.buildChangeDetails(f,this.data())),void 0)}}),a.fn.select2=function(){var d,e,f,g,h,c=Array.prototype.slice.call(arguments,0),i=["val","destroy","opened","open","close","focus","isFocused","container","dropdown","onSortStart","onSortEnd","enable","disable","readonly","positionDropdown","data","search"],j=["opened","isFocused","container","dropdown"],k=["val","data"],l={search:"externalSearch"};return this.each(function(){if(0===c.length||"object"==typeof c[0])d=0===c.length?{}:a.extend({},c[0]),d.element=a(this),"select"===d.element.get(0).tagName.toLowerCase()?h=d.element.prop("multiple"):(h=d.multiple||!1,"tags"in d&&(d.multiple=h=!0)),e=h?new window.Select2["class"].multi:new window.Select2["class"].single,e.init(d);else{if("string"!=typeof c[0])throw"Invalid arguments to select2 plugin: "+c;if(p(c[0],i)<0)throw"Unknown method: "+c[0];if(g=b,e=a(this).data("select2"),e===b)return;if(f=c[0],"container"===f?g=e.container:"dropdown"===f?g=e.dropdown:(l[f]&&(f=l[f]),g=e[f].apply(e,c.slice(1))),p(c[0],j)>=0||p(c[0],k)>=0&&1==c.length)return!1}}),g===b?this:g},a.fn.select2.defaults={width:"copy",loadMorePadding:0,closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c,d){var e=[];return E(this.text(a),c.term,e,d),e.join("")},transformVal:function(b){return a.trim(b)},formatSelection:function(a,c,d){return a?d(this.text(a)):b},sortResults:function(a){return a},formatResultCssClass:function(a){return a.css},formatSelectionCssClass:function(){return b},minimumResultsForSearch:0,minimumInputLength:0,maximumInputLength:null,maximumSelectionSize:0,id:function(a){return a==b?null:a.id},text:function(b){return b&&this.data&&this.data.text?a.isFunction(this.data.text)?this.data.text(b):b[this.data.text]:b.text
23
+ },matcher:function(a,b){return o(""+b).toUpperCase().indexOf(o(""+a).toUpperCase())>=0},separator:",",tokenSeparators:[],tokenizer:M,escapeMarkup:F,blurOnChange:!1,selectOnBlur:!1,adaptContainerCssClass:function(a){return a},adaptDropdownCssClass:function(){return null},nextSearchTerm:function(){return b},searchInputPlaceholder:"",createSearchChoicePosition:"top",shouldFocusInput:function(a){var b="ontouchstart"in window||navigator.msMaxTouchPoints>0;return b?a.opts.minimumResultsForSearch<0?!1:!0:!0}},a.fn.select2.locales=[],a.fn.select2.locales.en={formatMatches:function(a){return 1===a?"One result is available, press enter to select it.":a+" results are available, use up and down arrow keys to navigate."},formatNoMatches:function(){return"No matches found"},formatAjaxError:function(){return"Loading failed"},formatInputTooShort:function(a,b){var c=b-a.length;return"Please enter "+c+" or more character"+(1==c?"":"s")},formatInputTooLong:function(a,b){var c=a.length-b;return"Please delete "+c+" character"+(1==c?"":"s")},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results\u2026"},formatSearching:function(){return"Searching\u2026"}},a.extend(a.fn.select2.defaults,a.fn.select2.locales.en),a.fn.select2.ajaxDefaults={transport:a.ajax,params:{type:"GET",cache:!1,dataType:"json"}},window.Select2={query:{ajax:G,local:H,tags:I},util:{debounce:w,markMatch:E,escapeMarkup:F,stripDiacritics:o},"class":{"abstract":c,single:d,multi:e}}}}(jQuery);
lib/select2/select2.png ADDED
Binary file
lib/select2/select2x2.png ADDED
Binary file
package.json CHANGED
@@ -12,8 +12,10 @@
12
  "grunt-contrib-jasmine": "^0.8.2",
13
  "grunt-contrib-jshint": "^0.11.0",
14
  "grunt-contrib-watch": "^0.6.1",
 
15
  "grunt-sass": "^0.18.0",
16
  "grunt-wp-i18n": "^0.5.0",
 
17
  "remapify": "1.4.3"
18
  },
19
  "browserify": {
12
  "grunt-contrib-jasmine": "^0.8.2",
13
  "grunt-contrib-jshint": "^0.11.0",
14
  "grunt-contrib-watch": "^0.6.1",
15
+ "grunt-phpcs": "^0.4.0",
16
  "grunt-sass": "^0.18.0",
17
  "grunt-wp-i18n": "^0.5.0",
18
+ "grunt-wp-readme-to-markdown": "~0.9.0",
19
  "remapify": "1.4.3"
20
  },
21
  "browserify": {
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Shortcake (Shortcode UI) ===
2
- Contributors: mattheu, danielbachhuber, jitendraharpalani, sanchothefat, bfintal, davisshaver
3
  Tags: shortcodes
4
  Requires at least: 4.1
5
- Tested up to: 4.2
6
- Stable tag: 0.2.3
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -13,11 +13,15 @@ Shortcake makes using WordPress shortcodes a piece of cake.
13
 
14
  Used alongside `add_shortcode`, Shortcake supplies a user-friendly interface for adding a shortcode to a post, and viewing and editing it from within the content editor.
15
 
16
- See the [Github README](https://github.com/fusioneng/Shortcake/blob/master/README.md) for implementation notes. To report bugs or feature requests, [please use Github issues](https://github.com/fusioneng/Shortcake/issues).
 
 
17
 
18
  == Installation ==
19
 
20
- Shortcake can be installed like any other WordPress plugin. Once you've done so, [follow these integration steps](https://github.com/fusioneng/Shortcake#usage) to register UI for your shortcode.
 
 
21
 
22
  == Screenshots ==
23
 
@@ -26,8 +30,32 @@ Shortcake can be installed like any other WordPress plugin. Once you've done so,
26
  3. And add a user-friendly UI to edit shortcode content and attributes.
27
  4. Add new shortcodes to your post through "Add Media".
28
 
 
 
 
 
 
 
29
  == Changelog ==
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  = 0.2.3 (April 8, 2015) =
32
  * Fix WP 4.1 backwards compatibility issue by restoring arguments passed to TinyMCE view compatibility shim.
33
 
1
  === Shortcake (Shortcode UI) ===
2
+ Contributors: fusionengineering, mattheu, danielbachhuber, zebulonj, jitendraharpalani, sanchothefat, bfintal, davisshaver
3
  Tags: shortcodes
4
  Requires at least: 4.1
5
+ Tested up to: 4.2.1
6
+ Stable tag: 0.3.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
13
 
14
  Used alongside `add_shortcode`, Shortcake supplies a user-friendly interface for adding a shortcode to a post, and viewing and editing it from within the content editor.
15
 
16
+ Once you've installed the plugin, you'll need to [register UI for your shortcodes](https://github.com/fusioneng/Shortcake/wiki/Registering-Shortcode-UI). For inspiration, check out [examples of Shortcake in the wild](https://github.com/fusioneng/Shortcake/wiki/Shortcode-UI-Examples).
17
+
18
+ To report bugs or feature requests, [please use Github issues](https://github.com/fusioneng/Shortcake/issues).
19
 
20
  == Installation ==
21
 
22
+ Shortcake can be installed like any other WordPress plugin.
23
+
24
+ Once you've done so, you'll need to [register the UI for your code](https://github.com/fusioneng/Shortcake/wiki/Registering-Shortcode-UI).
25
 
26
  == Screenshots ==
27
 
30
  3. And add a user-friendly UI to edit shortcode content and attributes.
31
  4. Add new shortcodes to your post through "Add Media".
32
 
33
+ == Upgrade Notice ==
34
+
35
+ = 0.3 =
36
+
37
+ We've removed the compatibility shim for the magical `content` attribute. If you were using this to support editing inner content, you'll need to change your UI registration to use `inner_content`.
38
+
39
  == Changelog ==
40
 
41
+ = 0.3 (April 27, 2015) =
42
+ * **Breaking change**: We've removed the compatibility shim for the magical `content` attribute. If you were using this to support editing inner content, you'll need to change your UI registration to use `inner_content`.
43
+ * New `post_select` field type for selecting from a list of posts. Supports an additional `query` parameter to modify the search query.
44
+ * Using a new `post_type` argument, shortcode UI can be registered for specific post types. This is helpful if you want the UI for a given shortcode to only appear on specific post types.
45
+ * For each shortcode attribute, a `meta` argument can be specified to add arbitrary HTML attributes to the field. We've added a compatibility shim for the existing `placeholder` argument. This compatibility shim will be removed in v0.4.
46
+ * When inserting a shortcode, UI shows a helpful message when the shortcode doesn't have attributes to configure. Previously, the user was presented with a relatively blank screen.
47
+ * Our example plugin can be activated through the WordPress admin.
48
+ * Clicking "Insert Post Element" in the left menu effectively acts as back button to selecting a shortcode.
49
+ * Language around the editing experience reflects the shortcode you're editing. For instance, with a pullquote shortcode, "Edit Post Element" becomes "Edit Pullquote".
50
+ * Added Dutch translation.
51
+ * Source JavaScript files moved to `js/src` for clarity between source and built JavaScript.
52
+ * PHP files are scanned using PHP_CodeSniffer.
53
+ * Bug fix: Unquoted shortcode attributes are properly supported.
54
+ * Bug fix: Attachment field properly registers dependencies.
55
+ * Bug fix: "Insert Post Element" experience should work when visual editor is disabled. Shortcake is only loosely coupled with TinyMCE.
56
+ * Bug fix: Editor styles are loaded on `after_setup_theme` to prevent fatals.
57
+ * [Full release notes](http://fusion.net/story/126834/introducing-shortcake-v0-3-0-butter/).
58
+
59
  = 0.2.3 (April 8, 2015) =
60
  * Fix WP 4.1 backwards compatibility issue by restoring arguments passed to TinyMCE view compatibility shim.
61
 
screenshot-1.png CHANGED
Binary file
screenshot-2.png CHANGED
Binary file
screenshot-3.png CHANGED
Binary file
screenshot-4.png CHANGED
Binary file
shortcode-ui.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /**
3
  * Plugin Name: Shortcode UI
4
- * Version: v0.2.3
5
  * Description: User Interface for adding shortcodes.
6
  * Author: Fusion Engineering and community
7
  * Author URI: http://next.fusion.net/tag/shortcode-ui/
@@ -19,12 +19,13 @@
19
  * GNU General Public License for more details.
20
  */
21
 
22
- define( 'SHORTCODE_UI_VERSION', '0.2.3' );
23
 
24
  require_once dirname( __FILE__ ) . '/inc/class-shortcode-ui.php';
25
  require_once dirname( __FILE__ ) . '/inc/fields/class-shortcode-ui-fields.php';
26
  require_once dirname( __FILE__ ) . '/inc/fields/class-field-attachment.php';
27
  require_once dirname( __FILE__ ) . '/inc/fields/class-field-color.php';
 
28
 
29
  add_action( 'init', 'shortcode_ui_load_textdomain' );
30
 
@@ -34,6 +35,7 @@ add_action( 'init', function() {
34
  $fields = Shortcode_UI_Fields::get_instance();
35
  $attachment_field = Shortcake_Field_Attachment::get_instance();
36
  $color_field = Shortcake_Field_Color::get_instance();
 
37
 
38
  }, 5 );
39
 
1
  <?php
2
  /**
3
  * Plugin Name: Shortcode UI
4
+ * Version: 0.3.0
5
  * Description: User Interface for adding shortcodes.
6
  * Author: Fusion Engineering and community
7
  * Author URI: http://next.fusion.net/tag/shortcode-ui/
19
  * GNU General Public License for more details.
20
  */
21
 
22
+ define( 'SHORTCODE_UI_VERSION', '0.3.0' );
23
 
24
  require_once dirname( __FILE__ ) . '/inc/class-shortcode-ui.php';
25
  require_once dirname( __FILE__ ) . '/inc/fields/class-shortcode-ui-fields.php';
26
  require_once dirname( __FILE__ ) . '/inc/fields/class-field-attachment.php';
27
  require_once dirname( __FILE__ ) . '/inc/fields/class-field-color.php';
28
+ require_once dirname( __FILE__ ) . '/inc/fields/class-field-post-select.php';
29
 
30
  add_action( 'init', 'shortcode_ui_load_textdomain' );
31
 
35
  $fields = Shortcode_UI_Fields::get_instance();
36
  $attachment_field = Shortcake_Field_Attachment::get_instance();
37
  $color_field = Shortcake_Field_Color::get_instance();
38
+ $post_field = Shortcode_UI_Field_Post_Select::get_instance();
39
 
40
  }, 5 );
41