Timetable and Event Schedule by MotoPress - Version 2.3.4

Version Description

Download this release

Release Info

Developer MotoPress
Plugin Icon 128x128 Timetable and Event Schedule by MotoPress
Version 2.3.4
Comparing to
See all releases

Version 2.3.4

Files changed (120) hide show
  1. admin/help/index.php +68 -0
  2. admin/import/export.php +6 -0
  3. admin/import/footer.php +1 -0
  4. admin/import/greet.php +4 -0
  5. admin/import/header.php +2 -0
  6. admin/import/import.php +2 -0
  7. admin/import/index.php +10 -0
  8. classes/blocks/class-timetable-block.php +187 -0
  9. classes/class-controller.php +40 -0
  10. classes/class-core.php +714 -0
  11. classes/class-hooks.php +267 -0
  12. classes/class-model.php +41 -0
  13. classes/class-module.php +20 -0
  14. classes/class-permalinks.php +138 -0
  15. classes/class-preprocessor.php +216 -0
  16. classes/class-shortcode.php +386 -0
  17. classes/class-state-factory.php +84 -0
  18. classes/class-view.php +237 -0
  19. classes/controllers/class-controller-column.php +41 -0
  20. classes/controllers/class-controller-events.php +90 -0
  21. classes/controllers/class-controller-help.php +29 -0
  22. classes/controllers/class-controller-import.php +35 -0
  23. classes/controllers/class-controller-popup.php +36 -0
  24. classes/controllers/class-controller-settings.php +56 -0
  25. classes/libs/FirePHPCore/FirePHP.class.php +1785 -0
  26. classes/libs/FirePHPCore/FirePHP.class.php4 +1363 -0
  27. classes/libs/FirePHPCore/fb.php +287 -0
  28. classes/libs/FirePHPCore/fb.php4 +251 -0
  29. classes/libs/class-gump.php +2029 -0
  30. classes/libs/parsers.php +789 -0
  31. classes/models/class-column.php +206 -0
  32. classes/models/class-events.php +753 -0
  33. classes/models/class-export.php +479 -0
  34. classes/models/class-import.php +968 -0
  35. classes/models/class-settings.php +109 -0
  36. classes/modules/class-menu.php +33 -0
  37. classes/modules/class-post.php +100 -0
  38. classes/modules/class-taxonomy.php +42 -0
  39. classes/widgets/class-mp-timetable-widget.php +132 -0
  40. languages/mp-timetable-fa_IR.mo +0 -0
  41. languages/mp-timetable-fa_IR.po +962 -0
  42. languages/mp-timetable.pot +1008 -0
  43. languages/po2json.txt +1 -0
  44. media/css/admin.css +1 -0
  45. media/css/block-editor.css +1 -0
  46. media/css/images/column_icon.png +0 -0
  47. media/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  48. media/css/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  49. media/css/images/ui-bg_flat_10_000000_40x100.png +0 -0
  50. media/css/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  51. media/css/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  52. media/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  53. media/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  54. media/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  55. media/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  56. media/css/images/ui-icons_222222_256x240.png +0 -0
  57. media/css/images/ui-icons_228ef1_256x240.png +0 -0
  58. media/css/images/ui-icons_ef8c08_256x240.png +0 -0
  59. media/css/images/ui-icons_ffd27a_256x240.png +0 -0
  60. media/css/images/ui-icons_ffffff_256x240.png +0 -0
  61. media/css/jbox/jBox.css +569 -0
  62. media/css/jbox/themes/ModalBorder.css +46 -0
  63. media/css/jbox/themes/NoticeBorder.css +45 -0
  64. media/css/jbox/themes/TooltipBorder.css +33 -0
  65. media/css/jbox/themes/TooltipDark.css +37 -0
  66. media/css/jquery-ui-1.10.0.custom.min.css +5 -0
  67. media/css/jquery.ui.timepicker.css +57 -0
  68. media/css/spectrum.css +507 -0
  69. media/css/style.css +1 -0
  70. media/img/shortcode-icon.png +0 -0
  71. media/img/shortcode-icon.psd +0 -0
  72. media/js/blocks/dist/index.min.js +1 -0
  73. media/js/blocks/src/index.js +1 -0
  74. media/js/blocks/src/timetable/edit.js +88 -0
  75. media/js/blocks/src/timetable/index.js +29 -0
  76. media/js/blocks/src/timetable/inspector.js +269 -0
  77. media/js/events/event.js +937 -0
  78. media/js/events/event.min.js +1 -0
  79. media/js/lib/jBox.js +1574 -0
  80. media/js/lib/jBox.min.js +18 -0
  81. media/js/lib/jquery.ui.timepicker.js +1496 -0
  82. media/js/lib/jquery.ui.timepicker.min.js +40 -0
  83. media/js/lib/spectrum.js +2323 -0
  84. media/js/lib/spectrum.min.js +5 -0
  85. media/js/mce-timeTable-buttons.js +136 -0
  86. media/js/mce-timeTable-buttons.min.js +1 -0
  87. media/js/mptt-functions.js +370 -0
  88. media/js/mptt-functions.min.js +1 -0
  89. media/less/admin.less +116 -0
  90. media/less/block-editor.less +3 -0
  91. media/less/no-js.less +31 -0
  92. media/less/style.less +463 -0
  93. media/less/theme.less +162 -0
  94. mp-timetable.php +262 -0
  95. readme.txt +204 -0
  96. templates-functions/action-mp-column-functions.php +80 -0
  97. templates-functions/action-shortcode-functions.php +694 -0
  98. templates-functions/action-widget-functions.php +56 -0
  99. templates-functions/actions-mp-event-functions.php +202 -0
  100. templates/column/metabox-column-options.php +56 -0
  101. templates/events/column-category.php +6 -0
  102. templates/events/event-data.php +41 -0
  103. templates/events/metabox-event-data.php +79 -0
  104. templates/events/metabox-event-options.php +50 -0
  105. templates/events/no-script.php +1 -0
  106. templates/popup/index.php +181 -0
  107. templates/settings/general.php +33 -0
  108. templates/shortcodes/empty-search-events.php +1 -0
  109. templates/shortcodes/event-container.php +54 -0
  110. templates/shortcodes/index-timetable.php +8 -0
  111. templates/shortcodes/table-header.php +10 -0
  112. templates/single-mp-column.php +28 -0
  113. templates/single-mp-event.php +37 -0
  114. templates/taxonomies/taxonomy-link.php +1 -0
  115. templates/templates-actions/action-sidebar.php +19 -0
  116. templates/theme/column-events.php +48 -0
  117. templates/theme/event-timeslots.php +45 -0
  118. templates/theme/widget-upcoming-view.php +59 -0
  119. templates/widgets/gallery-list.php +151 -0
  120. templates/widgets/widget-view.php +96 -0
admin/help/index.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h1 class="wp-heading-inline">Help</h1>
3
+ <p class="alignright"><?php
4
+
5
+ $filePath = Mp_Time_Table::get_plugin_path() . Mp_Time_Table::get_plugin_name() . '.php';
6
+ $pluginObject = get_plugin_data( $filePath );
7
+ $name = $pluginObject[ 'Name' ];
8
+
9
+ echo sprintf(
10
+ /* translators: 1: Timetable and Event Schedule 2:: five stars rating */
11
+ __( 'If you like %1$s please leave us a %2$s rating.', 'mp-timetable' ),
12
+ sprintf( '<strong>%s</strong>', esc_html( $name ) ),
13
+ '<a href="https://wordpress.org/support/plugin/mp-timetable/reviews?rate=5#new-post" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
14
+ );
15
+ ?></p>
16
+ <hr/>
17
+ <h2>Quick Start Guide</h2>
18
+ <ol>
19
+ <li>
20
+ <p><strong>Add Columns</strong><br/>
21
+ Make sure you created Columns prior to adding Events for the events to be then assigned to the created columns.</p>
22
+ </li>
23
+ <li>
24
+ <p><strong>Add Events</strong><br/>
25
+ The events will be displayed in the actual timetable. Event details will be shown on each event’s individual page.</p>
26
+ <ol>
27
+ <li>
28
+ <p><strong>Add Event Category</strong><br/>
29
+ Events can be presented under different categories, which can be chosen in the shortcode parameters below.</p>
30
+ </li>
31
+ </ol>
32
+ </li>
33
+ <li>
34
+ <p><strong>Add Timetable to a Page</strong></p>
35
+ <ol>
36
+ <li>Find "TimeTable" icon on TinyMCE panel in Classic Editor .</li>
37
+ <li>Build Timetable shortcode manually.
38
+ <p>Shortcode <code>[mp-timetable ... ]</code> attributes:</p>
39
+ <ul>
40
+ <li><code>col</code> - comma-separated column IDs.</li>
41
+ <li><code>events</code> - comma-separated event IDs.</li>
42
+ <li><code>event_categ</code> - comma-separated event category IDs.</li>
43
+ <li><code>increment</code> - hour measure; possible values <kbd>1</kbd> - hour (1h), <kbd>0.5</kbd> - half an hour (30min), <kbd>0.25</kbd> - quarter an hour (15min).</li>
44
+ <li><code>title</code> - display event title; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
45
+ <li><code>time</code> - display event time; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
46
+ <li><code>sub-title</code> - display event subtitle; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
47
+ <li><code>description</code> - display event description; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
48
+ <li><code>user</code> - display event head; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
49
+ <li><code>row_height</code> - event block height in pixels; example <kbd>45</kbd></li>
50
+ <li><code>font_size</code> - base font size for the table; example <kbd>12px</kbd>, <kbd>2em</kbd>, <kbd>80%</kbd>.</li>
51
+ <li><code>view</code> - filter style; possible values <kbd>dropdown_list</kbd> or <kbd>tabs</kbd>.</li>
52
+ <li><code>label</code> - filter label; default is <kbd>All Events</kbd>.</li>
53
+ <li><code>hide_label</code> - display 'All Events' label or not; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
54
+ <li><code>hide_hrs</code> - hide first (hours) column; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
55
+ <li><code>hide_empty_rows</code> - hide empty rows; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
56
+ <li><code>group</code> - merge cells with common events; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
57
+ <li><code>disable_event_url</code> - disable event URL; possible values <kbd>1</kbd> or <kbd>0</kbd>.</li>
58
+ <li><code>text_align</code> - horizontal align; possible values <kbd>left</kbd>, <kbd>center</kbd>, <kbd>right</kbd>.</li>
59
+ <li><code>text_align_vertical</code> - vertical align ; possible values <kbd>default</kbd>, <kbd>top</kbd>, <kbd>middle</kbd>, <kbd>bottom</kbd>.</li>
60
+ <li><code>id</code> - unique ID.</li>
61
+ <li><code>custom_class</code> - CSS class.</li>
62
+ <li><code>responsive</code> - mobile layout; possible values <kbd>1</kbd> - display as list, <kbd>0</kbd> - display as table.</li>
63
+ </ul>
64
+ </li>
65
+ </ol>
66
+ </li>
67
+ </ol>
68
+ </div>
admin/import/export.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <h3><?php _e('Export', 'mp-timetable') ?></h3>
2
+ <form novalidate="novalidate" method="post" id="mptt_export">
3
+ <input type="hidden" name="controller" value="import">
4
+ <input type="hidden" name="mptt_action" value="export">
5
+ <p class="submit"><input type="submit" value="<?php _e('Export', 'mp-timetable') ?>" class="button button-primary" id="submit" name="submit"></p>
6
+ </form>
admin/import/footer.php ADDED
@@ -0,0 +1 @@
 
1
+ </div>
admin/import/greet.php ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <div class="narrow">
2
+ <p><?php _e('Choose a WXR (.xml) file to upload, then click Upload file and import.', 'mp-timetable') ?></p>
3
+ <?php wp_import_upload_form('admin.php?import=mptt-importer&amp;step=1') ?>
4
+ </div>
admin/import/header.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <div class="wrap">
2
+ <h1 class="wp-heading-inline"><?php _e('Import / Export Timetable Plugin Data', 'mp-timetable') ?></h1>
admin/import/import.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <h3><?php _e('Import', 'mp-timetable') ?></h3>
2
+ <?php wp_import_upload_form('admin.php?import=mptt-importer&amp;step=1'); ?>
admin/import/index.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use mp_timetable\plugin_core\classes\View as View;
3
+
4
+ View::get_instance()->render_html('../admin/import/header', $data);
5
+
6
+ View::get_instance()->render_html('../admin/import/export', $data);
7
+
8
+ View::get_instance()->render_html('../admin/import/import', $data);
9
+
10
+ View::get_instance()->render_html('../admin/import/footer', $data);
classes/blocks/class-timetable-block.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\blocks;
4
+
5
+ use Mp_Time_Table;
6
+ use mp_timetable\plugin_core\classes\Core;
7
+ use mp_timetable\plugin_core\classes\Shortcode;
8
+
9
+ class Timetable_Block {
10
+
11
+ public function __construct() {
12
+
13
+ // block-js
14
+ wp_register_script(
15
+ 'mptt-blocks-js',
16
+ Mp_Time_Table::get_plugin_url( '/media/js/blocks/dist/index.min.js' ),
17
+ array( 'wp-i18n', 'wp-editor', 'wp-element', 'wp-blocks', 'wp-components', 'wp-api', 'wp-api-fetch', 'mptt-functions', 'mptt-event-object'),
18
+ Core::get_instance()->get_version()
19
+ );
20
+
21
+ // style.css
22
+ wp_register_style(
23
+ 'mptt-blocks',
24
+ Mp_Time_Table::get_plugin_url( 'media/css/style.css' ),
25
+ array(),
26
+ Core::get_instance()->get_version()
27
+ );
28
+
29
+ // block-editor.css
30
+ wp_register_style(
31
+ 'mptt-blocks-editor',
32
+ Mp_Time_Table::get_plugin_url( '/media/css/block-editor.css' ),
33
+ array('mptt-blocks'),
34
+ Core::get_instance()->get_version()
35
+ );
36
+
37
+ // internationalization
38
+ wp_set_script_translations( 'mptt-blocks-js', 'mp-timetable', Mp_Time_Table::get_plugin_path() . 'languages' );
39
+
40
+ register_block_type(
41
+ 'mp-timetable/timetable',
42
+ array(
43
+ 'attributes' => array(
44
+ 'align' => array(
45
+ 'type' => 'string',
46
+ ),
47
+ 'col' => array(
48
+ 'type' => 'array',
49
+ 'items' => [
50
+ 'type' => 'integer',
51
+ ],
52
+ ),
53
+ 'events' => array(
54
+ 'type' => 'array',
55
+ 'items' => [
56
+ 'type' => 'integer',
57
+ ],
58
+ ),
59
+ 'event_categ' => array(
60
+ 'type' => 'array',
61
+ 'items' => [
62
+ 'type' => 'integer',
63
+ ],
64
+ ),
65
+ 'increment' => array(
66
+ 'type' => 'string',
67
+ 'default' => '1',
68
+ ),
69
+ 'view' => array(
70
+ 'type' => 'string',
71
+ 'default' => 'dropdown_list',
72
+ ),
73
+ 'label' => array(
74
+ 'type' => 'string',
75
+ 'default' => __( "All Events", 'mp-timetable' ),
76
+ ),
77
+ 'hide_label' => array(
78
+ 'type' => 'string',
79
+ 'default' => '0',
80
+ ),
81
+ 'hide_hrs' => array(
82
+ 'type' => 'string',
83
+ 'default' => '0',
84
+ ),
85
+ 'hide_empty_rows' => array(
86
+ 'type' => 'string',
87
+ 'default' => '1',
88
+ ),
89
+ 'title' => array(
90
+ 'type' => 'string',
91
+ 'default' => '1',
92
+ ),
93
+ 'time' => array(
94
+ 'type' => 'string',
95
+ 'default' => '1',
96
+ ),
97
+ 'sub_title' => array(
98
+ 'type' => 'string',
99
+ 'default' => '0',
100
+ ),
101
+ 'description' => array(
102
+ 'type' => 'string',
103
+ 'default' => '1',
104
+ ),
105
+ 'user' => array(
106
+ 'type' => 'string',
107
+ 'default' => '0',
108
+ ),
109
+ 'group' => array(
110
+ 'type' => 'string',
111
+ 'default' => '0',
112
+ ),
113
+ 'disable_event_url' => array(
114
+ 'type' => 'string',
115
+ 'default' => '0',
116
+ ),
117
+ 'text_align' => array(
118
+ 'type' => 'string',
119
+ 'default' => 'center',
120
+ ),
121
+ 'id' => array(
122
+ 'type' => 'string',
123
+ ),
124
+ 'row_height' => array(
125
+ 'type' => 'string',
126
+ 'default' => '45',
127
+ ),
128
+ 'font_size' => array(
129
+ 'type' => 'string',
130
+ ),
131
+ 'responsive' => array(
132
+ 'type' => 'string',
133
+ 'default' => '1',
134
+ ),
135
+ 'text_align_vertical' => array(
136
+ 'type' => 'string',
137
+ 'default' => 'default',
138
+ ),
139
+ 'custom_class' => array(
140
+ 'type' => 'string',
141
+ ),
142
+ ),
143
+ 'render_callback' => [ $this, 'render_timetable' ],
144
+ 'editor_style' => 'mptt-blocks-editor',
145
+ 'editor_script' => 'mptt-blocks-js',
146
+ )
147
+ );
148
+ }
149
+
150
+ private function show_shortcode($attributes) {
151
+ foreach ($attributes as $key => $value) {
152
+ // [] -> '1,2,3'
153
+ if ( is_array($value) ) {
154
+ $attributes[$key] = implode( ',', $value );
155
+ }
156
+ // 'sub_title' -> 'sub-title'
157
+ if ($key == 'sub_title') {
158
+ $attributes['sub-title'] = $attributes[$key];
159
+ unset( $attributes[$key] );
160
+ }
161
+ }
162
+
163
+ echo Shortcode::get_instance()->show_shortcode($attributes);
164
+ }
165
+
166
+ public function render_timetable( $attributes ) {
167
+
168
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
169
+
170
+ $block_name = 'wp-block-timetable';
171
+
172
+ $class = $block_name;
173
+ if ( isset( $attributes['align'] ) ) {
174
+ $class .= ' align' . $attributes['align'];
175
+ }
176
+
177
+ ob_start();
178
+ ?><div class="<?php echo esc_attr( $class ); ?>"><?php
179
+
180
+ $this->show_shortcode($attributes);
181
+
182
+ ?></div><?php
183
+
184
+ $result = ob_get_clean();
185
+ return $result;
186
+ }
187
+ }
classes/class-controller.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ /**
6
+ * Controller class
7
+ */
8
+ class Controller extends Core {
9
+
10
+ protected static $instance;
11
+
12
+ public static function get_instance() {
13
+ if (null === self::$instance) {
14
+ self::$instance = new self();
15
+ }
16
+ return self::$instance;
17
+ }
18
+
19
+ /**
20
+ * Install controllers
21
+ */
22
+ public function install() {
23
+ // include all core controllers
24
+ Core::include_all(\Mp_Time_Table::get_plugin_part_path('classes/controllers'));
25
+ }
26
+
27
+ /**
28
+ * Send json data
29
+ *
30
+ * @param array $data
31
+ */
32
+ public function send_json($data) {
33
+ if (is_array($data) && isset($data['success']) && !$data['success']) {
34
+ wp_send_json_error($data);
35
+ } else {
36
+ wp_send_json_success($data['data']);
37
+ }
38
+ }
39
+
40
+ }
classes/class-core.php ADDED
@@ -0,0 +1,714 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ use Mp_Time_Table;
6
+ use mp_timetable\classes\models\Column;
7
+ use mp_timetable\classes\models\Events;
8
+
9
+ /**
10
+ * Class main state
11
+ */
12
+ class Core {
13
+
14
+ protected static $instance;
15
+
16
+ protected $version;
17
+
18
+ /**
19
+ * Current state
20
+ */
21
+ private $state;
22
+
23
+ /**
24
+ * Core constructor.
25
+ */
26
+ public function __construct() {
27
+
28
+ $this->taxonomy_names = array(
29
+ 'mp-event_category',
30
+ 'mp-event_tag'
31
+ );
32
+ $this->post_types = array(
33
+ 'mp-event',
34
+ 'mp-column'
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Check for ajax post
40
+ * @return bool
41
+ */
42
+ static function is_ajax() {
43
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
44
+ return true;
45
+ } else {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @return array
52
+ */
53
+ public function get_post_types() {
54
+ return $this->post_types;
55
+ }
56
+
57
+ /**
58
+ * @return array
59
+ */
60
+ public function get_taxonomy_names() {
61
+ return $this->taxonomy_names;
62
+ }
63
+
64
+ /**
65
+ * Init current plugin
66
+ *
67
+ * @param $name
68
+ */
69
+ public function init_plugin( $name ) {
70
+ load_plugin_textdomain( 'mp-timetable', false, Mp_Time_Table::get_plugin_path() . 'languages/' );
71
+
72
+ // include template for function
73
+ Core::include_all( Mp_Time_Table::get_plugin_part_path( 'templates-functions' ) );
74
+
75
+ // include plugin models files
76
+ Model::get_instance()->install();
77
+
78
+ // include plugin controllers files
79
+ Controller::get_instance()->install();
80
+
81
+ // include plugin Preprocessors files
82
+ Preprocessor::install();
83
+
84
+ // include plugin modules
85
+ Module::install();
86
+
87
+ // install state
88
+ $this->install_state( $name );
89
+
90
+ // init all hooks
91
+ Hooks::get_instance()->install_hooks();
92
+ Hooks::get_instance()->register_template_action();
93
+ }
94
+
95
+ /**
96
+ * Include all files from folder
97
+ *
98
+ * @param string $folder
99
+ * @param boolean $inFolder
100
+ */
101
+ static function include_all( $folder, $inFolder = true ) {
102
+ if ( file_exists( $folder ) ) {
103
+ $includeArr = scandir( $folder );
104
+ foreach ( $includeArr as $include ) {
105
+ if ( ! is_dir( $folder . "/" . $include ) ) {
106
+ include_once( $folder . "/" . $include );
107
+ } else {
108
+ if ( $include != "." && $include != ".." && $inFolder ) {
109
+ self::include_all( $folder . "/" . $include );
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Install current state
118
+ *
119
+ * @param $name
120
+ */
121
+ public function install_state( $name ) {
122
+ // include plugin state
123
+ Core::get_instance()->set_state( new State_Factory( $name ) );
124
+ }
125
+
126
+ /**
127
+ * @return Core
128
+ */
129
+ public static function get_instance() {
130
+ if ( null === self::$instance ) {
131
+ self::$instance = new self();
132
+ }
133
+
134
+ return self::$instance;
135
+ }
136
+
137
+ /**
138
+ * Include pseudo template
139
+ *
140
+ * @param $template
141
+ *
142
+ * @return string
143
+ */
144
+ public function modify_single_template( $template ) {
145
+
146
+ global $post;
147
+
148
+ if ( ! empty( $post ) && in_array( $post->post_type, $this->post_types ) ) {
149
+ add_action( 'loop_start', array( $this, 'setup_pseudo_template' ) );
150
+ }
151
+
152
+ return $template;
153
+ }
154
+
155
+ /**
156
+ * Setup pseudo template
157
+ *
158
+ * @param object $query
159
+ */
160
+ public function setup_pseudo_template( $query ) {
161
+
162
+ global $post;
163
+
164
+ if ( $query->is_main_query() ) {
165
+ if ( ! empty( $post ) && in_array( $post->post_type, $this->post_types ) ) {
166
+ add_filter( 'the_content', array( $this, 'append_post_meta' ) );
167
+ }
168
+ remove_action( 'loop_start', array( $this, 'setup_pseudo_template' ) );
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Append post meta
174
+ *
175
+ * @param $content
176
+ *
177
+ * @return string
178
+ */
179
+ public function append_post_meta( $content ) {
180
+ // run only once
181
+ remove_filter( 'the_content', array( $this, 'append_post_meta' ) );
182
+
183
+ global $post;
184
+
185
+ ob_start();
186
+ switch ( $post->post_type ) {
187
+ case 'mp-event':
188
+ Events::get_instance()->render_event_metas();
189
+ break;
190
+ case 'mp-column':
191
+ Column::get_instance()->render_column_metas();
192
+ break;
193
+ }
194
+ $append = ob_get_clean();
195
+ $content .= $append;
196
+
197
+ return $content;
198
+ }
199
+
200
+ /**
201
+ * Get model instace
202
+ *
203
+ * @param bool|false $type
204
+ *
205
+ * @return bool|mixed
206
+ */
207
+ public function get( $type = false ) {
208
+ $state = false;
209
+ if ( $type ) {
210
+ $state = $this->get_model( $type );
211
+ }
212
+
213
+ return $state;
214
+ }
215
+
216
+ /**
217
+ * Check and return current state
218
+ *
219
+ * @param string $type
220
+ *
221
+ * @return boolean
222
+ */
223
+ public function get_model( $type = null ) {
224
+ return Core::get_instance()->get_state()->get_model( $type );
225
+ }
226
+
227
+ /**
228
+ * Get State
229
+ * @return bool
230
+ */
231
+ public function get_state() {
232
+ if ( $this->state ) {
233
+ return $this->state;
234
+ } else {
235
+ return false;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Set state
241
+ *
242
+ * @param $state
243
+ */
244
+ public function set_state( $state ) {
245
+ $this->state = $state;
246
+ }
247
+
248
+ /**
249
+ * Get version
250
+ * @return mixed
251
+ */
252
+ public function get_version() {
253
+ if ( empty( $this->version ) ) {
254
+ $this->init_plugin_version();
255
+ }
256
+
257
+ return $this->version;
258
+ }
259
+
260
+ /**
261
+ * Init plugin version
262
+ */
263
+ public function init_plugin_version() {
264
+
265
+ $filePath = Mp_Time_Table::get_plugin_path() . Mp_Time_Table::get_plugin_name() . '.php';
266
+
267
+ if ( ! function_exists( 'get_plugin_data' ) ) {
268
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
269
+ }
270
+
271
+ $pluginObject = get_plugin_data( $filePath );
272
+ $this->version = $pluginObject[ 'Version' ];
273
+ }
274
+
275
+ /**
276
+ * Get controller
277
+ *
278
+ * @param $type
279
+ *
280
+ * @return mixed
281
+ */
282
+ public function get_controller( $type ) {
283
+ return Core::get_instance()->get_state()->get_controller( $type );
284
+ }
285
+
286
+ /**
287
+ * Get view
288
+ *
289
+ * @return View
290
+ */
291
+ public function get_view() {
292
+ return View::get_instance();
293
+ }
294
+
295
+ /**
296
+ * Get preprocessor
297
+ *
298
+ * @param $type
299
+ *
300
+ * @return mixed
301
+ */
302
+ public function get_preprocessor( $type = null ) {
303
+ return Core::get_instance()->get_state()->get_preprocessor( $type );
304
+ }
305
+
306
+ /**
307
+ * Route plugin url
308
+ */
309
+ public function wp_ajax_route_url() {
310
+ $controller = isset( $_REQUEST[ "controller" ] ) ? $_REQUEST[ "controller" ] : null;
311
+ $action = isset( $_REQUEST[ "mptt_action" ] ) ? $_REQUEST[ "mptt_action" ] : null;
312
+
313
+ if ( ! empty( $action ) ) {
314
+ // call controller
315
+ Preprocessor::get_instance()->call_controller( $action, $controller );
316
+ die();
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Register taxonomies
322
+ */
323
+ public function register_all_taxonomies() {
324
+
325
+ $permalinks = Core::get_instance()->get_permalink_structure();
326
+
327
+ do_action( 'mptt_before_register_taxonomies' );
328
+
329
+ $event_category_labels = array(
330
+ 'name' => __( 'Event categories', 'mp-timetable' ),
331
+ 'singular_name' => __( 'Event category', 'mp-timetable' ),
332
+ 'add_new' => __( 'Add New Event category', 'mp-timetable' ),
333
+ 'add_new_item' => __( 'Add New Event category', 'mp-timetable' ),
334
+ 'edit_item' => __( 'Edit Event category', 'mp-timetable' ),
335
+ 'new_item' => __( 'New Event category', 'mp-timetable' ),
336
+ 'all_items' => __( 'All Event categories', 'mp-timetable' ),
337
+ 'view_item' => __( 'View Event category', 'mp-timetable' ),
338
+ 'search_items' => __( 'Search Event category', 'mp-timetable' ),
339
+ 'not_found' => __( 'No Event categories found', 'mp-timetable' ),
340
+ 'not_found_in_trash' => __( 'No Event categories found in Trash', 'mp-timetable' ),
341
+ 'menu_name' => __( 'Event categories', 'mp-timetable' )
342
+ );
343
+
344
+ $event_category_args = array(
345
+ 'label' => __( 'Event categories', 'mp-timetable' ),
346
+ 'labels' => $event_category_labels,
347
+ 'show_in_rest' => true,
348
+ 'public' => true,
349
+ 'show_in_nav_menus' => true,
350
+ 'show_ui' => true,
351
+ 'show_in_menu' => false,
352
+ 'show_tagcloud' => true,
353
+ 'hierarchical' => true,
354
+ 'update_count_callback' => '',
355
+ 'rewrite' => array(
356
+ 'slug' => $permalinks['event_category_base'], //'timetable/category'
357
+ 'with_front' => false,
358
+ 'hierarchical' => true
359
+ ),
360
+ 'capabilities' => array(),
361
+ 'meta_box_cb' => null,
362
+ 'show_admin_column' => false,
363
+ '_builtin' => false,
364
+ 'show_in_quick_edit' => null,
365
+ );
366
+
367
+ register_taxonomy(
368
+ 'mp-event_category',
369
+ apply_filters( 'mptt_taxonomy_objects_event_category', array( 'mp-event' ) ),
370
+ apply_filters( 'mptt_taxonomy_args_event_category', $event_category_args)
371
+ );
372
+
373
+ $event_tag_labels = array(
374
+ 'name' => __( 'Event tags', 'mp-timetable' ),
375
+ 'singular_name' => __( 'Event tag', 'mp-timetable' ),
376
+ 'add_new' => __( 'Add New Event tag', 'mp-timetable' ),
377
+ 'add_new_item' => __( 'Add New Event tag', 'mp-timetable' ),
378
+ 'edit_item' => __( 'Edit Event tag', 'mp-timetable' ),
379
+ 'new_item' => __( 'New Event tag', 'mp-timetable' ),
380
+ 'all_items' => __( 'All Event tags', 'mp-timetable' ),
381
+ 'view_item' => __( 'View Event tag', 'mp-timetable' ),
382
+ 'search_items' => __( 'Search Event tag', 'mp-timetable' ),
383
+ 'not_found' => __( 'No Event tags found', 'mp-timetable' ),
384
+ 'not_found_in_trash' => __( 'No Event tags found in Trash', 'mp-timetable' ),
385
+ 'menu_name' => __( 'Event tags', 'mp-timetable' )
386
+ );
387
+
388
+ $event_tag_args = array(
389
+ 'label' => __( 'Event tags', 'mp-timetable' ),
390
+ 'labels' => $event_tag_labels,
391
+ 'public' => true,
392
+ 'show_in_nav_menus' => true,
393
+ 'show_ui' => true,
394
+ 'show_in_menu' => false,
395
+ 'show_tagcloud' => true,
396
+ 'hierarchical' => false,
397
+ 'update_count_callback' => '',
398
+ 'rewrite' => array(
399
+ 'slug' => $permalinks['event_tag_base'], //'timetable/tag'
400
+ 'with_front' => false,
401
+ 'hierarchical' => true
402
+ ),
403
+ 'capabilities' => array(),
404
+ 'meta_box_cb' => null,
405
+ 'show_admin_column' => false,
406
+ '_builtin' => false,
407
+ 'show_in_quick_edit' => null,
408
+ );
409
+
410
+ register_taxonomy(
411
+ 'mp-event_tag',
412
+ apply_filters( 'mptt_taxonomy_objects_event_tag', array( 'mp-event' ) ),
413
+ apply_filters( 'mptt_taxonomy_args_event_tag', $event_tag_args)
414
+ );
415
+
416
+ do_action( 'mptt_after_register_taxonomies' );
417
+ }
418
+
419
+ /**
420
+ * Register custom post type
421
+ */
422
+ public function register_all_post_type() {
423
+
424
+ $permalinks = Core::get_instance()->get_permalink_structure();
425
+
426
+ do_action( 'mptt_before_register_post_types' );
427
+
428
+ register_post_type(
429
+ 'mp-event',
430
+ apply_filters(
431
+ 'mptt_register_post_type_event',
432
+ array(
433
+ 'labels' => array(
434
+ 'name' => __( 'Events', 'mp-timetable' ),
435
+ 'singular_name' => __( 'Event', 'mp-timetable' ),
436
+ 'add_new' => __( 'Add New Event', 'mp-timetable' ),
437
+ 'add_new_item' => __( 'Add New Event', 'mp-timetable' ),
438
+ 'edit_item' => __( 'Edit Event', 'mp-timetable' ),
439
+ 'new_item' => __( 'New Event', 'mp-timetable' ),
440
+ 'all_items' => __( 'All Events', 'mp-timetable' ),
441
+ 'view_item' => __( 'View Event', 'mp-timetable' ),
442
+ 'search_items' => __( 'Search Event', 'mp-timetable' ),
443
+ 'not_found' => __( 'No Events found', 'mp-timetable' ),
444
+ 'not_found_in_trash' => __( 'No Events found in Trash', 'mp-timetable' ),
445
+ 'menu_name' => __( 'Events', 'mp-timetable' )
446
+ ),
447
+ 'public' => true,
448
+ 'show_in_rest' => true,
449
+ 'show_ui' => true,
450
+ 'show_in_menu' => false,
451
+ 'show_in_nav_menus' => true,
452
+ 'capability_type' => 'post',
453
+ 'menu_position' => 21,
454
+ 'hierarchical' => false,
455
+ 'has_archive' => true,
456
+ 'rewrite' => array(
457
+ 'slug' => $permalinks['event_base'], //'timetable/event'
458
+ 'with_front' => false,
459
+ 'hierarchical' => true
460
+ ),
461
+ 'supports' => array( 'title', 'editor', 'comments', 'excerpt', 'author', 'thumbnail', 'page-attributes' ),
462
+ 'show_in_admin_bar' => true
463
+ )
464
+ )
465
+ );
466
+
467
+ register_post_type(
468
+ 'mp-column',
469
+ apply_filters(
470
+ 'mptt_register_post_type_column',
471
+ array(
472
+ 'labels' => array(
473
+ 'name' => __( 'Columns', 'mp-timetable' ),
474
+ 'singular_name' => __( 'Column', 'mp-timetable' ),
475
+ 'add_new' => __( 'Add New Column', 'mp-timetable' ),
476
+ 'add_new_item' => __( 'Add New Column', 'mp-timetable' ),
477
+ 'edit_item' => __( 'Edit Column', 'mp-timetable' ),
478
+ 'new_item' => __( 'New Column', 'mp-timetable' ),
479
+ 'all_items' => __( 'All Columns', 'mp-timetable' ),
480
+ 'view_item' => __( 'View Column', 'mp-timetable' ),
481
+ 'search_items' => __( 'Search Column', 'mp-timetable' ),
482
+ 'not_found' => __( 'No Columns found', 'mp-timetable' ),
483
+ 'not_found_in_trash' => __( 'No Columns found in Trash', 'mp-timetable' ),
484
+ 'menu_name' => __( 'Columns', 'mp-timetable' )
485
+ ),
486
+ 'public' => true,
487
+ 'show_in_rest' => true,
488
+ 'show_ui' => true,
489
+ 'show_in_menu' => false,
490
+ 'show_in_nav_menus' => true,
491
+ 'capability_type' => 'post',
492
+ 'menu_position' => 21,
493
+ 'hierarchical' => false,
494
+ 'has_archive' => true,
495
+ 'rewrite' => array(
496
+ 'slug' => $permalinks['column_base'], //'timetable/column'
497
+ 'with_front' => false,
498
+ 'hierarchical' => true
499
+ ),
500
+ 'supports' => array( 'title', 'editor', 'page-attributes' ),
501
+ 'show_in_admin_bar' => true
502
+ )
503
+ )
504
+ );
505
+
506
+ do_action( 'mptt_after_register_post_types' );
507
+
508
+ }
509
+
510
+ /**
511
+ * Create Plugin table if not exists
512
+ */
513
+ public function create_table() {
514
+
515
+ global $wpdb;
516
+
517
+ $charset_collate = $wpdb->get_charset_collate();
518
+
519
+ $table_name = Mp_Time_Table::get_datatable();
520
+
521
+ $sql = "CREATE TABLE IF NOT EXISTS $table_name (
522
+ `id` int(11) NOT NULL AUTO_INCREMENT,
523
+ `column_id` int(11) NOT NULL,
524
+ `event_id` int(11) NOT NULL,
525
+ `event_start` time NOT NULL,
526
+ `event_end` time NOT NULL,
527
+ `user_id` int(11) NOT NULL,
528
+ `description` text NOT NULL,
529
+ PRIMARY KEY (`id`),
530
+ UNIQUE KEY `id` (`id`)
531
+ ) $charset_collate";
532
+
533
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
534
+ dbDelta( $sql );
535
+ }
536
+
537
+ /**
538
+ * Hook admin_enqueue_scripts
539
+ */
540
+ public function admin_enqueue_scripts() {
541
+ global $current_screen;
542
+ $this->current_screen( $current_screen );
543
+ }
544
+
545
+ /**
546
+ * Load script by current screen
547
+ *
548
+ * @param \WP_Screen $current_screen
549
+ */
550
+ public function current_screen( \WP_Screen $current_screen ) {
551
+ wp_register_script( 'mptt-event-object', Mp_Time_Table::get_plugin_url( 'media/js/events/event' . $this->get_prefix() . '.js' ), array( 'jquery' ), $this->version );
552
+ wp_localize_script(
553
+ 'mptt-event-object',
554
+ 'MPTT',
555
+ array( 'table_class' => apply_filters( 'mptt_shortcode_static_table_class', 'mptt-shortcode-table' ) )
556
+ );
557
+
558
+ wp_enqueue_script( 'underscore' );
559
+ wp_enqueue_style( 'mptt-admin-style', Mp_Time_Table::get_plugin_url( 'media/css/admin.css' ), array(), $this->version );
560
+
561
+ wp_enqueue_script( 'mptt-functions', Mp_Time_Table::get_plugin_url( 'media/js/mptt-functions' . $this->get_prefix() . '.js' ), array(), $this->version );
562
+
563
+ if ( ! empty( $current_screen ) ) {
564
+ switch ( $current_screen->id ) {
565
+ case 'mp-event':
566
+ wp_enqueue_script( 'spectrum', Mp_Time_Table::get_plugin_url( 'media/js/lib/spectrum' . $this->get_prefix() . '.js' ), array( 'jquery' ), '1.8.0' );
567
+ wp_enqueue_script( 'mptt-event-object' );
568
+ wp_enqueue_script( 'jquery-ui-timepicker', Mp_Time_Table::get_plugin_url( 'media/js/lib/jquery.ui.timepicker' . $this->get_prefix() . '.js' ), '0.3.3' );
569
+
570
+ wp_enqueue_style( 'jquery-ui-core', Mp_Time_Table::get_plugin_url( 'media/css/jquery-ui-1.10.0.custom.min.css' ), array(), '1.10.0' );
571
+ wp_enqueue_style( 'spectrum', Mp_Time_Table::get_plugin_url( 'media/css/spectrum.css' ), array(), '1.8.0' );
572
+ wp_enqueue_style( 'jquery-ui-timepicker', Mp_Time_Table::get_plugin_url( 'media/css/jquery.ui.timepicker.css' ), array(), '0.3.3' );
573
+ break;
574
+
575
+ case 'mp-column':
576
+ wp_enqueue_script( 'jquery-ui-datepicker' );
577
+ wp_enqueue_script( 'mptt-event-object' );
578
+
579
+ wp_enqueue_style( 'jquery-ui-core', Mp_Time_Table::get_plugin_url( 'media/css/jquery-ui-1.10.0.custom.min.css' ), array(), '1.10.0' );
580
+ break;
581
+
582
+ case 'customize':
583
+ case 'widgets':
584
+ wp_enqueue_script( 'spectrum', Mp_Time_Table::get_plugin_url( 'media/js/lib/spectrum' . $this->get_prefix() . '.js' ), array( 'jquery' ), '1.8.0' );
585
+ wp_enqueue_script( 'mptt-event-object' );
586
+
587
+ wp_enqueue_style( 'jquery-ui-core', Mp_Time_Table::get_plugin_url( 'media/css/jquery-ui-1.10.0.custom.min.css' ), array(), '1.10.0' );
588
+ wp_enqueue_style( 'spectrum', Mp_Time_Table::get_plugin_url( 'media/css/spectrum.css' ), array(), '1.8.0' );
589
+ break;
590
+ }
591
+
592
+ switch ( $current_screen->base ) {
593
+ case 'post':
594
+ case 'page':
595
+ wp_enqueue_script( 'jquery-ui-tabs' );
596
+ wp_enqueue_script( 'jBox', Mp_Time_Table::get_plugin_url( 'media/js/lib/jBox' . $this->get_prefix() . '.js' ), array( 'jquery' ), '0.2.1' );
597
+
598
+ wp_enqueue_style( 'jBox', Mp_Time_Table::get_plugin_url( 'media/css/jbox/jBox.css' ), array(), '1.8.0' );
599
+ break;
600
+
601
+ case 'options-permalink':
602
+ if ( apply_filters('mptt_permalinks_enabled', true) ) {
603
+ $permalinks = new Permalinks();
604
+ }
605
+ break;
606
+
607
+ default:
608
+ break;
609
+ }
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Get prefix
615
+ *
616
+ * @return string
617
+ */
618
+ public function get_prefix() {
619
+ global $is_IE;
620
+
621
+ $prefix = ! MP_TT_DEBUG ? '.min' : '';
622
+
623
+ if ($is_IE){
624
+ $prefix = '';
625
+ }
626
+
627
+ return $prefix;
628
+ }
629
+
630
+ /**
631
+ * Hook wp_enqueue_scripts
632
+ */
633
+ public function wp_enqueue_scripts() {
634
+ if ( ! empty( $_GET[ 'motopress-ce' ] ) ) {
635
+ $this->add_plugin_js( 'shortcode' );
636
+ }
637
+ }
638
+
639
+ /**
640
+ * Add plugin js
641
+ *
642
+ * @param bool $type
643
+ */
644
+ public function add_plugin_js( $type = false ) {
645
+ wp_register_script( 'mptt-event-object', Mp_Time_Table::get_plugin_url( 'media/js/events/event' . $this->get_prefix() . '.js' ), array( 'jquery', 'mptt-functions' ), $this->version );
646
+ wp_localize_script(
647
+ 'mptt-event-object',
648
+ 'MPTT',
649
+ array( 'table_class' => apply_filters( 'mptt_shortcode_static_table_class', 'mptt-shortcode-table' ) )
650
+ );
651
+
652
+ switch ( $type ) {
653
+ case 'shortcode':
654
+ case 'widget':
655
+ wp_enqueue_script( 'underscore' );
656
+ wp_enqueue_script( 'mptt-functions', Mp_Time_Table::get_plugin_url( 'media/js/mptt-functions' . $this->get_prefix() . '.js' ), array( 'jquery' ), $this->version );
657
+ wp_enqueue_script( 'mptt-event-object' );
658
+ break;
659
+ }
660
+ }
661
+
662
+ /**
663
+ * Add plugin css
664
+ */
665
+ public function add_plugin_css() {
666
+ wp_enqueue_style( 'mptt-style', Mp_Time_Table::get_plugin_url( 'media/css/style.css' ), array(), $this->version );
667
+ }
668
+
669
+ /**
670
+ * Fix fatal error for earlier WP versions
671
+ *
672
+ * @return bool
673
+ */
674
+ public function is_embed() {
675
+ global $wp_version;
676
+
677
+ if ( ! function_exists( 'is_embed' ) ) {
678
+ return false;
679
+ }
680
+
681
+ if ( version_compare( $wp_version, '4.4', '<' ) ) {
682
+ if ( ! function_exists( 'is_embed' ) ) {
683
+ return false;
684
+ }
685
+ }
686
+
687
+ return is_embed();
688
+ }
689
+
690
+ /**
691
+ * Get permalink settings
692
+ *
693
+ * @return array
694
+ */
695
+ public function get_permalink_structure() {
696
+
697
+ $saved_permalinks = (array) get_option( 'mp_timetable_permalinks', array() );
698
+
699
+ $permalinks = wp_parse_args(
700
+ array_filter( $saved_permalinks ), array(
701
+ 'column_base' => 'timetable/column',
702
+ 'event_base' => 'timetable/event',
703
+ 'event_category_base' => 'timetable/category',
704
+ 'event_tag_base' => 'timetable/tag',
705
+ )
706
+ );
707
+
708
+ if ( $saved_permalinks !== $permalinks ) {
709
+ update_option( 'mp_timetable_permalinks', $permalinks );
710
+ }
711
+
712
+ return $permalinks;
713
+ }
714
+ }
classes/class-hooks.php ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ use Mp_Time_Table;
6
+ use mp_timetable\classes\models\Import;
7
+ use mp_timetable\classes\models\Settings;
8
+ use mp_timetable\plugin_core\classes\modules\Post;
9
+ use mp_timetable\classes\blocks\Timetable_Block;
10
+
11
+ /**
12
+ * Class Hooks
13
+ * @package mp_timetable\plugin_core\classes
14
+ */
15
+ class Hooks extends Core {
16
+
17
+ protected static $instance;
18
+
19
+ /**
20
+ * @return Hooks
21
+ */
22
+ public static function get_instance() {
23
+ if ( null === self::$instance ) {
24
+ self::$instance = new self();
25
+ }
26
+
27
+ return self::$instance;
28
+ }
29
+
30
+ /**
31
+ * Install hooks
32
+ */
33
+ public function install_hooks() {
34
+ // register custom post type and taxonomies
35
+ add_action( 'init', array( $this, "init" ) );
36
+ add_action( 'wp_enqueue_scripts', array( Core::get_instance(), "add_plugin_css" ) );
37
+ add_action( 'wp_head', array( $this, "set_html_js_class" ) );
38
+
39
+ add_action( 'admin_init', array( $this->get_controller( 'settings' ), 'action_save' ) );
40
+ add_action( "admin_init", array( $this, "admin_init" ) );
41
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
42
+ add_action( 'manage_posts_custom_column', array( $this->get( 'events' ), 'get_event_taxonomy' ) );
43
+ add_action( 'manage_posts_custom_column', array( $this->get( 'column' ), 'get_column_columns' ) );
44
+ add_action( 'current_screen', array( Core::get_instance(), 'current_screen' ) );
45
+ add_action( 'pre_get_posts', array( $this->get( 'column' ), 'clientarea_default_order' ), 9 );
46
+ //add media in frontend WP
47
+ add_action( 'wp_enqueue_scripts', array( Core::get_instance(), "wp_enqueue_scripts" ) );
48
+ //add media in admin WP
49
+ add_action( 'admin_enqueue_scripts', array( Core::get_instance(), "admin_enqueue_scripts" ) );
50
+ add_action( 'widgets_init', array( $this, 'register_widgets' ) );
51
+ // Manage event/column columns
52
+ add_filter( 'manage_edit-mp-event_columns', array( $this->get( 'events' ), 'set_event_columns' ) );
53
+ add_filter( 'manage_edit-mp-column_columns', array( $this->get( 'column' ), 'set_column_columns' ) );
54
+ // post_class filter
55
+ add_filter( 'post_class', 'mptt_post_class', 15, 3 );
56
+ // to display events with other posts on author page
57
+ add_filter( 'pre_get_posts', array( Post::get_instance(), 'pre_get_posts' ), 9 );
58
+ }
59
+
60
+ /**
61
+ * Register widgets and sidebar
62
+ */
63
+ public function register_widgets() {
64
+ $template = get_option( 'template' );
65
+ if ( $template != 'twentyfourteen' && Settings::get_instance()->is_plugin_template_mode() ) {
66
+ register_sidebar( array(
67
+ 'name' => __( 'Timetable Sidebar', 'mp-timetable' ),
68
+ 'id' => "mptt-sidebar",
69
+ 'description' => __( 'Timetable', 'mp-timetable' ),
70
+ 'class' => 'sidebar-container',
71
+ 'before_widget' => '<li id="%1$s" class="widget %2$s">',
72
+ 'after_widget' => "</li>\n",
73
+ 'before_title' => '<h2 class="widget-title">',
74
+ 'after_title' => "</h2>\n"
75
+ ) );
76
+ }
77
+ register_widget( 'timetable\classes\widgets\Timetable_widget' );
78
+ }
79
+
80
+ /**
81
+ * Register template_action
82
+ */
83
+ public function register_template_action() {
84
+ add_action( 'mptt_sidebar', 'mptt_sidebar', 10 );
85
+ add_filter( 'mptt_widget_settings', 'mptt_widget_settings', 10, 1 );
86
+ add_action( 'mptt-single-mp-column-before-wrapper', 'mptt_theme_wrapper_before' );
87
+ add_action( 'mptt-single-mp-column-after-wrapper', 'mptt_theme_wrapper_after' );
88
+ add_action( 'mptt-single-mp-event-before-wrapper', 'mptt_theme_wrapper_before' );
89
+ add_action( 'mptt-single-mp-event-after-wrapper', 'mptt_theme_wrapper_after' );
90
+
91
+ // Event template action
92
+ add_action( 'mptt_event_item_content', 'mptt_event_template_content_title', 10 );
93
+ add_action( 'mptt_event_item_content', 'mptt_event_template_content_thumbnail', 20 );
94
+ add_action( 'mptt_event_item_content', 'mptt_event_template_content_post_content', 30 );
95
+ add_action( 'mptt_event_item_content', 'mptt_event_template_content_time_title', 40 );
96
+ add_action( 'mptt_event_item_content', 'mptt_event_template_content_time_list', 50 );
97
+ add_action( 'mptt_event_item_content', 'mptt_event_template_content_comments', 60 );
98
+
99
+ // Column template action
100
+ add_action( 'mptt_single_column_template_content', 'mptt_column_template_content_title', 10 );
101
+ add_action( 'mptt_single_column_template_content', 'mptt_column_template_content_post_content', 20 );
102
+ add_action( 'mptt_single_column_template_content', 'mptt_column_template_content_events_list', 30 );
103
+
104
+ //Shortcode template action
105
+ add_action( 'mptt_shortcode_template_before_content', 'mptt_shortcode_template_before_content', 10 );
106
+ add_action( 'mptt_shortcode_template_content', 'mptt_shortcode_template_content_filter', 10 );
107
+ add_action( 'mptt_shortcode_template_content', 'mptt_shortcode_template_content_static_table', 20 );
108
+ add_action( 'mptt_shortcode_template_content', 'mptt_shortcode_template_content_responsive_table', 30 );
109
+ add_action( 'mptt_shortcode_template_after_content', 'mptt_shortcode_template_after_content', 10 );
110
+
111
+ // Widget actions
112
+ add_action( 'mptt_widget_template_before_content', 'mptt_widget_template_before_content', 10 );
113
+ add_action( 'mptt_widget_template_content', 'mptt_widget_template_content', 10 );
114
+ add_action( 'mptt_widget_template_after_content', 'mptt_widget_template_after_content', 10 );
115
+
116
+ }
117
+
118
+ /**
119
+ * Init hook
120
+ */
121
+ public function init() {
122
+
123
+ // Init sort codes
124
+ Shortcode::get_instance()->init();
125
+ // Register post type
126
+ Core::get_instance()->register_all_post_type();
127
+ // Register taxonomy all
128
+ Core::get_instance()->register_all_taxonomies();
129
+ // route url
130
+ Core::get_instance()->wp_ajax_route_url();
131
+
132
+ if ( Settings::get_instance()->is_plugin_template_mode() ) {
133
+ // plugin mode
134
+ add_filter( 'template_include', array( View::get_instance(), 'template_loader' ), 99 );
135
+ } else {
136
+ //theme mode
137
+ add_filter( 'single_template', array( Core::get_instance(), 'modify_single_template' ), 99 );
138
+ }
139
+
140
+ add_action( 'mp_library', array( Shortcode::get_instance(), 'integration_motopress' ), 20, 1 );
141
+
142
+ Core::get_instance()->init_plugin_version();
143
+
144
+ add_filter( 'body_class', array( $this, 'browser_body_class' ) );
145
+ add_filter( 'the_tags', array( $this->get( 'events' ), 'the_tags' ), 10, 5 );
146
+ add_filter( 'the_category', array( $this->get( 'events' ), 'the_category' ), 10, 3 );
147
+
148
+ if ( function_exists('register_block_type') ) {
149
+ new Timetable_Block();
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Hooks for admin panel
155
+ */
156
+ public function admin_init() {
157
+ //add buttons to mce
158
+ add_filter( "mce_external_plugins", array( $this, "mce_external_plugins" ) );
159
+ add_filter( 'mce_buttons', array( $this, "mce_buttons" ) );
160
+
161
+ Core::get_instance()->init_plugin_version();
162
+
163
+ add_action( 'before_delete_post', array( Post::get_instance(), 'before_delete_custom_post' ) );
164
+ add_action( 'add_meta_boxes', array( Post::get_instance(), 'add_meta_boxes' ) );
165
+ add_action( 'save_post', array( Post::get_instance(), 'save_custom_post' ), 40, 2 );
166
+ add_action( 'wp_ajax_route_url', array( Core::get_instance(), "wp_ajax_route_url" ) );
167
+
168
+ register_importer( 'mptt-importer', 'Timetable', __( 'Import Timetable events, categories, tags and images.' ), array( Import::get_instance(), 'import' ) );
169
+ }
170
+
171
+ /**
172
+ * Registered page in admin wp
173
+ */
174
+ public function admin_menu() {
175
+
176
+ //Timetable
177
+ add_menu_page( __( 'Timetable', 'mp-timetable' ), __( 'Timetable', 'mp-timetable' ),
178
+ 'edit_posts', 'edit.php?post_type=mp-event', '', 'dashicons-calendar', '59.51' );
179
+
180
+ //Events
181
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Events', 'mp-timetable' ), __( 'Events', 'mp-timetable' ),
182
+ 'edit_posts', 'edit.php?post_type=mp-event' );
183
+
184
+ //Add Event
185
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Add Event', 'mp-timetable' ), __( 'Add Event', 'mp-timetable' ),
186
+ 'edit_posts', 'post-new.php?post_type=mp-event' );
187
+
188
+ //Columns
189
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Columns', 'mp-timetable' ), __( 'Columns', 'mp-timetable' ),
190
+ 'edit_posts', 'edit.php?post_type=mp-column' );
191
+
192
+ //Add Column
193
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Add Column', 'mp-timetable' ), __( 'Add Column', 'mp-timetable' ),
194
+ 'edit_posts', 'post-new.php?post_type=mp-column' );
195
+
196
+ //Event Categories
197
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Event Categories', 'mp-timetable' ), __( 'Event Categories', 'mp-timetable' ),
198
+ 'manage_categories', 'edit-tags.php?taxonomy=mp-event_category&amp;post_type=mp-event' );
199
+
200
+ //Event Tags
201
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Event Tags', 'mp-timetable' ), __( 'Event Tags', 'mp-timetable' ),
202
+ 'manage_categories', 'edit-tags.php?taxonomy=mp-event_tag&amp;post_type=mp-event' );
203
+
204
+ //Settings
205
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Settings', 'mp-timetable' ), __( 'Settings', 'mp-timetable' ),
206
+ 'switch_themes', 'mptt-settings', array( $this->get_controller( 'settings' ), 'action_content' ) );
207
+
208
+ //Export / Import
209
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Export / Import', 'mp-timetable' ), __( 'Export / Import', 'mp-timetable' ),
210
+ 'import', 'mptt-import', array( $this->get_controller( 'import' ), 'action_content' ) );
211
+
212
+ //Help
213
+ add_submenu_page( 'edit.php?post_type=mp-event', __( 'Help', 'mp-timetable' ), __( 'Help', 'mp-timetable' ),
214
+ 'read', 'mptt-help', array( $this->get_controller( 'help' ), 'action_content' ) );
215
+ }
216
+
217
+ /**
218
+ * Connect js for MCE editor
219
+ *
220
+ * @param $plugin_array
221
+ *
222
+ * @return mixed
223
+ */
224
+ public function mce_external_plugins( $plugin_array ) {
225
+ $path = Mp_Time_Table::get_plugin_url( 'media/js/mce-timeTable-buttons' . $this->get_prefix() . '.js' );
226
+ $plugin_array[ 'mp_timetable' ] = $path;
227
+
228
+ return $plugin_array;
229
+ }
230
+
231
+ /**
232
+ * Add button in MCE editor
233
+ *
234
+ * @param $buttons
235
+ *
236
+ * @return mixed
237
+ */
238
+ public function mce_buttons( $buttons ) {
239
+ array_push( $buttons, 'addTimeTableButton' );
240
+
241
+ return $buttons;
242
+ }
243
+
244
+ /**
245
+ * Browser body class
246
+ *
247
+ * @param $classes
248
+ *
249
+ * @return array
250
+ */
251
+ public function browser_body_class( $classes ) {
252
+ global $is_IE;
253
+
254
+ if ( $is_IE ) {
255
+ $classes[] = 'mprm_ie_browser';
256
+ }
257
+
258
+ return $classes;
259
+ }
260
+
261
+ /**
262
+ * Set js
263
+ */
264
+ public function set_html_js_class() {
265
+ echo View::get_instance()->get_template_html('events/no-script');
266
+ }
267
+ }
classes/class-model.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ /**
6
+ * Model class
7
+ */
8
+ class Model extends Core {
9
+
10
+ protected static $instance;
11
+
12
+ /**
13
+ * @return Model
14
+ */
15
+ public static function get_instance() {
16
+ if (null === self::$instance) {
17
+ self::$instance = new self();
18
+ }
19
+ return self::$instance;
20
+ }
21
+
22
+ /**
23
+ * Install models by type
24
+ */
25
+ public function install() {
26
+ // include all core models
27
+ Core::include_all(\Mp_Time_Table::get_plugin_part_path('classes/models'));
28
+ }
29
+
30
+ /**
31
+ * Get return Array
32
+ *
33
+ * @param array $data
34
+ * @param bool|false $success
35
+ *
36
+ * @return array
37
+ */
38
+ public function get_arr($data = array(), $success = false) {
39
+ return array('success' => $success, 'data' => $data);
40
+ }
41
+ }
classes/class-module.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ use Mp_Time_Table;
6
+
7
+ /**
8
+ * Class Module
9
+ * @package mp_timetable\plugin_core\classes
10
+ */
11
+ class Module extends Core {
12
+
13
+ /**
14
+ * Install controllers
15
+ */
16
+ public static function install() {
17
+ // include all core controllers
18
+ Core::include_all(Mp_Time_Table::get_plugin_part_path('classes/modules'));
19
+ }
20
+ }
classes/class-permalinks.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ class Permalinks {
6
+
7
+ /**
8
+ * Permalink settings.
9
+ *
10
+ * @var array
11
+ */
12
+ private $permalinks = array();
13
+
14
+ /**
15
+ * Hook in tabs.
16
+ */
17
+ public function __construct() {
18
+ $this->settings_init();
19
+ $this->settings_save();
20
+ }
21
+
22
+ /**
23
+ * Init our settings.
24
+ */
25
+ public function settings_init() {
26
+ add_settings_section( 'mp-timetable-permalinks', __( 'Timetable Permalinks', 'mp-timetable' ), array( $this, 'settings' ), 'permalink' );
27
+
28
+ add_settings_field(
29
+ 'timetable_column_slug',
30
+ __( 'Column base', 'mp-timetable' ),
31
+ array( $this, 'timetable_column_slug_input' ),
32
+ 'permalink',
33
+ 'mp-timetable-permalinks'
34
+ );
35
+ add_settings_field(
36
+ 'timetable_event_slug',
37
+ __( 'Event base', 'mp-timetable' ),
38
+ array( $this, 'timetable_event_slug_input' ),
39
+ 'permalink',
40
+ 'mp-timetable-permalinks'
41
+ );
42
+ add_settings_field(
43
+ 'timetable_event_category_slug',
44
+ __( 'Event Category base', 'mp-timetable' ),
45
+ array( $this, 'timetable_event_category_slug_input' ),
46
+ 'permalink',
47
+ 'mp-timetable-permalinks'
48
+ );
49
+ add_settings_field(
50
+ 'timetable_event_tag_slug',
51
+ __( 'Event Tag base', 'mp-timetable' ),
52
+ array( $this, 'timetable_event_tag_slug_input' ),
53
+ 'permalink',
54
+ 'mp-timetable-permalinks'
55
+ );
56
+
57
+ $this->permalinks = Core::get_instance()->get_permalink_structure();
58
+ }
59
+
60
+ /**
61
+ * Show a Column slug input box.
62
+ */
63
+ public function timetable_column_slug_input() {
64
+ ?>
65
+ <input name="timetable_column_slug" type="text" class="regular-text" value="<?php echo esc_attr( $this->permalinks['column_base'] ); ?>" placeholder="timetable/column" />
66
+ <?php
67
+ }
68
+
69
+ /**
70
+ * Show an Event slug input box.
71
+ */
72
+ public function timetable_event_slug_input() {
73
+ ?>
74
+ <input name="timetable_event_slug" type="text" class="regular-text" value="<?php echo esc_attr( $this->permalinks['event_base'] ); ?>" placeholder="timetable/event" />
75
+ <?php
76
+ }
77
+
78
+ /**
79
+ * Show an Event Category slug input box.
80
+ */
81
+ public function timetable_event_category_slug_input() {
82
+ ?>
83
+ <input name="timetable_event_category_slug" type="text" class="regular-text" value="<?php echo esc_attr( $this->permalinks['event_category_base'] ); ?>" placeholder="timetable/category" />
84
+ <?php
85
+ }
86
+
87
+ /**
88
+ * Show an Event Tag slug input box.
89
+ */
90
+ public function timetable_event_tag_slug_input() {
91
+ ?>
92
+ <input name="timetable_event_tag_slug" type="text" class="regular-text" value="<?php echo esc_attr( $this->permalinks['event_tag_base'] ); ?>" placeholder="timetable/tag" />
93
+ <?php
94
+ }
95
+
96
+ /**
97
+ * Show the settings.
98
+ */
99
+ public function settings() {
100
+ wp_nonce_field( 'timetable-permalinks', 'timetable-permalinks-nonce' );
101
+ }
102
+
103
+ /**
104
+ * Save the settings.
105
+ */
106
+ public function settings_save() {
107
+ if ( ! is_admin() ) {
108
+ return;
109
+ }
110
+
111
+ // We need to save the options ourselves; settings api does not trigger save for the permalinks page.
112
+ if ( isset( $_POST['permalink_structure'],
113
+ $_POST['timetable-permalinks-nonce'],
114
+ $_POST['timetable_column_slug'],
115
+ $_POST['timetable_event_slug'],
116
+ $_POST['timetable_event_category_slug'],
117
+ $_POST['timetable_event_tag_slug']
118
+ ) && wp_verify_nonce( wp_unslash( $_POST['timetable-permalinks-nonce'] ), 'timetable-permalinks' )
119
+ ) { // WPCS: input var ok, sanitization ok.
120
+
121
+ $permalinks = (array) get_option( 'mp_timetable_permalinks', array() );
122
+
123
+ $permalinks['column_base'] = $this->sanitize_permalink( wp_unslash( $_POST['timetable_column_slug'] ) ); // WPCS: input var ok, sanitization ok.
124
+ $permalinks['event_base'] = $this->sanitize_permalink( wp_unslash( $_POST['timetable_event_slug'] ) ); // WPCS: input var ok, sanitization ok.
125
+ $permalinks['event_category_base'] = $this->sanitize_permalink( wp_unslash( $_POST['timetable_event_category_slug'] ) ); // WPCS: input var ok, sanitization ok.
126
+ $permalinks['event_tag_base'] = $this->sanitize_permalink( wp_unslash( $_POST['timetable_event_tag_slug'] ) ); // WPCS: input var ok, sanitization ok.
127
+
128
+ update_option( 'mp_timetable_permalinks', $permalinks );
129
+ }
130
+ }
131
+
132
+ private function sanitize_permalink( $value ) {
133
+
134
+ $value = esc_url_raw( trim( $value ) );
135
+ $value = str_replace( 'http://', '', $value );
136
+ return untrailingslashit( $value );
137
+ }
138
+ }
classes/class-preprocessor.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ use mp_timetable\classes\libs\FirePHPCore\FB;
6
+ use mp_timetable\classes\libs\GUMP;
7
+ use \Mp_Time_Table;
8
+
9
+ class Preprocessor extends GUMP {
10
+
11
+ protected static $instance;
12
+
13
+ public static function get_instance() {
14
+ if (null === self::$instance) {
15
+ self::$instance = new self();
16
+ }
17
+ return self::$instance;
18
+ }
19
+
20
+ /**
21
+ * Install Preprocessors
22
+ */
23
+ static function install() {
24
+ Core::include_all(Mp_Time_Table::get_plugin_part_path('classes/preprocessors'));
25
+ }
26
+
27
+ /**
28
+ * Fatal error handler
29
+ *
30
+ * @param $buffer
31
+ *
32
+ * @return mixed
33
+ */
34
+ static function fatal_error_handler($buffer) {
35
+ $error = error_get_last();
36
+ if (!empty($error)) {
37
+ switch ($error['type']) {
38
+ case E_WARNING:
39
+ $type = 'warning';
40
+ break;
41
+ case E_NOTICE:
42
+ $type = 'notice';
43
+ break;
44
+ case E_ERROR:
45
+ $type = 'fatal error';
46
+ break;
47
+ case E_USER_NOTICE:
48
+ $type = 'core error';
49
+ break;
50
+ default :
51
+ $type = 'error';
52
+ break;
53
+ }
54
+ FB::error($_REQUEST, "$type REQUEST");
55
+ FB::error($error, $type);
56
+ }
57
+ return $buffer;
58
+ }
59
+
60
+ /**
61
+ * Call controller
62
+ *
63
+ * @param string $action
64
+ * @param bool|false $page
65
+ *
66
+ * @return mixed
67
+ */
68
+ public function call_controller($action = 'content', $page = false) {
69
+ if (empty($page)) {
70
+ trigger_error("Wrong controller ");
71
+ }
72
+ $path = Mp_Time_Table::get_plugin_part_path('classes/controllers/');
73
+ // if controller exists
74
+ if ('controller' != $page && !file_exists("{$path}class-controller-{$page}.php")) {
75
+ $ControllerName = 'Controller_' . ucfirst($page);
76
+ if (class_exists($ControllerName)) {
77
+ trigger_error("Wrong controller {$path}class-controller-{$page}.php");
78
+ }
79
+ }
80
+ $action = "action_$action";
81
+ $controller = Core::get_instance()->get_state()->get_controller($page);
82
+ // if method exists
83
+ if (method_exists($controller, $action)) {
84
+ return $controller->$action();
85
+ } else {
86
+ trigger_error("Wrong {$action} in {$path}class-controller-{$page}.php");
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Progress
92
+ *
93
+ * @param array $params
94
+ * @param $name
95
+ * @param $type
96
+ *
97
+ * @return array
98
+ */
99
+ protected function progress(array $params, $name, $type) {
100
+ $success = $this->run($params);
101
+ if ($success !== false) {
102
+ return Core::get_instance()->get_model($type)->$name($params);
103
+ } else {
104
+ $name = "on_error_{$name}";
105
+ if (!method_exists($this, $name)) {
106
+ $name = 'get_errors_array';
107
+ }
108
+ return array('success' => $success, 'data' => $this->$name());
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Process the validation errors and return an array of errors with field names as keys.
114
+ *
115
+ * @param $convert_to_string
116
+ *
117
+ * @return array | null (if empty)
118
+ */
119
+ public function get_errors_array($convert_to_string = null) {
120
+ if (empty($this->errors)) {
121
+ return ($convert_to_string) ? null : array();
122
+ }
123
+
124
+ $resp = array();
125
+
126
+ foreach ($this->errors as $e) {
127
+ $field = ucwords(str_replace(array('_', '-'), chr(32), $e['field']));
128
+ $param = $e['param'];
129
+
130
+ // Let's fetch explicit field names if they exist
131
+ if (array_key_exists($e['field'], self::$fields)) {
132
+ $field = self::$fields[$e['field']];
133
+ }
134
+
135
+ switch ($e['rule']) {
136
+ case 'mismatch' :
137
+ $resp[$e['field']] = "There is no validation rule for $field";
138
+ break;
139
+ case 'validate_required':
140
+ $resp[$e['field']] = "Enter $field field";
141
+ break;
142
+ case 'validate_valid_email':
143
+ $resp[$e['field']] = "Enter a valid email address";
144
+ break;
145
+ case 'validate_max_len':
146
+ $resp[$e['field']] = "The $field field needs to be $param or shorter in length";
147
+ break;
148
+ case 'validate_min_len':
149
+ $resp[$e['field']] = "The $field field needs to be $param or longer in length";
150
+ break;
151
+ case 'validate_exact_len':
152
+ $resp[$e['field']] = "The $field field needs to be exactly $param characters in length";
153
+ break;
154
+ case 'validate_alpha':
155
+ $resp[$e['field']] = "The $field field may only contain alpha characters(a-z)";
156
+ break;
157
+ case 'validate_alpha_numeric':
158
+ $resp[$e['field']] = "The $field field may only contain alpha-numeric characters";
159
+ break;
160
+ case 'validate_alpha_dash':
161
+ $resp[$e['field']] = "The $field field may only contain alpha characters &amp; dashes";
162
+ break;
163
+ case 'validate_numeric':
164
+ $resp[$e['field']] = "The $field field may only contain numeric characters";
165
+ break;
166
+ case 'validate_integer':
167
+ $resp[$e['field']] = "The $field field may only contain a numeric value";
168
+ break;
169
+ case 'validate_boolean':
170
+ $resp[$e['field']] = "The $field field may only contain a true or false value";
171
+ break;
172
+ case 'validate_float':
173
+ $resp[$e['field']] = "The $field field may only contain a float value";
174
+ break;
175
+ case 'validate_valid_url':
176
+ $resp[$e['field']] = "The $field field is required to be a valid URL";
177
+ break;
178
+ case 'validate_url_exists':
179
+ $resp[$e['field']] = "The $field URL does not exist";
180
+ break;
181
+ case 'validate_valid_ip':
182
+ $resp[$e['field']] = "The $field field needs to contain a valid IP address";
183
+ break;
184
+ case 'validate_valid_cc':
185
+ $resp[$e['field']] = "The $field field needs to contain a valid credit card number";
186
+ break;
187
+ case 'validate_valid_name':
188
+ $resp[$e['field']] = "The $field field needs to contain a valid human name";
189
+ break;
190
+ case 'validate_contains':
191
+ $resp[$e['field']] = "The $field field needs to contain one of these values: " . implode(', ', $param);
192
+ break;
193
+ case 'validate_street_address':
194
+ $resp[$e['field']] = "The $field field needs to be a valid street address";
195
+ break;
196
+ case 'validate_date':
197
+ $resp[$e['field']] = "The $field field needs to be a valid date";
198
+ break;
199
+ case 'validate_min_numeric':
200
+ $resp[$e['field']] = "The $field field needs to be a numeric value, equal to, or higher than $param";
201
+ break;
202
+ case 'validate_max_numeric':
203
+ $resp[$e['field']] = "The $field field needs to be a numeric value, equal to, or lower than $param";
204
+ break;
205
+ case 'validate_min_age':
206
+ $resp[$e['field']] = "The $field field needs to have an age greater than or equal to $param";
207
+ break;
208
+ default:
209
+ $resp[$e['field']] = "The $field field is invalid";
210
+ }
211
+ }
212
+
213
+ return $resp;
214
+ }
215
+
216
+ }
classes/class-shortcode.php ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ use mp_timetable\classes\models\Column;
6
+ use mp_timetable\classes\models\Events;
7
+
8
+ /**
9
+ * Class Shortcode
10
+ *
11
+ * @package mp_timetable\plugin_core\classes
12
+ */
13
+ class Shortcode extends Core {
14
+
15
+ protected static $instance;
16
+
17
+ /**
18
+ * Shortcode constructor.
19
+ */
20
+ public function __construct() {
21
+ $this->init_plugin_version();
22
+ parent::__construct();
23
+ }
24
+
25
+ /**
26
+ * Return instance
27
+ *
28
+ * @return Shortcode
29
+ */
30
+ public static function get_instance() {
31
+ if ( null === self::$instance ) {
32
+ self::$instance = new self();
33
+ }
34
+
35
+ return self::$instance;
36
+ }
37
+
38
+ /**
39
+ * Init
40
+ */
41
+ public function init() {
42
+ add_shortcode( 'mp-timetable', array( $this, "show_shortcode" ) );
43
+ }
44
+
45
+ /**
46
+ * Show shortcode
47
+ *
48
+ * @param $params
49
+ *
50
+ * @return mixed
51
+ */
52
+ public function show_shortcode( $params ) {
53
+ global $mptt_shortcode_data;
54
+
55
+ $this->add_plugin_js( 'shortcode' );
56
+
57
+ if ( empty( $params ) ) {
58
+ $params = array();
59
+ }
60
+
61
+ $mptt_shortcode_data = array();
62
+
63
+ $mptt_shortcode_data[ 'params' ] = $params = shortcode_atts( array(
64
+ 'events' => "",
65
+ 'event_categ' => "",
66
+ 'col' => "",
67
+ 'increment' => "1",
68
+ 'view' => "dropdown_list",
69
+ 'label' => __( "All Events", 'mp-timetable' ),
70
+ 'hide_label' => "0",
71
+ 'title' => "0",
72
+ 'time' => "0",
73
+ 'group' => "0",
74
+ 'sub-title' => "0",
75
+ 'description' => "0",
76
+ 'user' => "0",
77
+ 'hide_hrs' => "0",
78
+ 'hide_empty_rows' => "1",
79
+ 'text_align_vertical' => "default",
80
+ 'row_height' => "45",
81
+ 'font_size' => "",
82
+ 'disable_event_url' => "0",
83
+ 'text_align' => "center",
84
+ 'id' => "",
85
+ 'custom_class' => "",
86
+ 'responsive' => "1"
87
+ ), $params );
88
+
89
+ $mptt_shortcode_data[ 'events_data' ] = $this->get_shortcode_events( $params );
90
+
91
+ if ( ! empty( $mptt_shortcode_data[ 'events_data' ] ) ) {
92
+ $mptt_shortcode_data[ 'unique_events' ] = $mptt_shortcode_data[ 'events_data' ][ 'unique_events' ];
93
+ if ( isset( $mptt_shortcode_data[ 'events_data' ][ 'events' ] ) ) {
94
+ unset( $mptt_shortcode_data[ 'events_data' ][ 'events' ] );
95
+ }
96
+ }
97
+
98
+ if ( empty( $mptt_shortcode_data[ 'events_data' ][ 'events' ] ) && empty( $mptt_shortcode_data[ 'events_data' ][ 'column' ] ) ) {
99
+ return $this->get_view()->get_template_html( 'shortcodes/empty-search-events', array() );
100
+ } else {
101
+ return $this->get_view()->get_template_html( 'shortcodes/index-timetable', array() );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Get shortcode events
107
+ *
108
+ * @param $params
109
+ *
110
+ * @return array
111
+ */
112
+ public function get_shortcode_events( $params ) {
113
+ $columns_ids = $events_categ = $events = array();
114
+
115
+ $step = $params[ 'increment' ] === '1' ? 60 : ( 60 * $params[ 'increment' ] );
116
+
117
+ $events_data = array( 'events' => array(), 'column' => array() );
118
+
119
+ //get event by id
120
+ if ( ! empty( $params[ 'events' ] ) && empty( $params[ 'col' ] ) && empty( $params[ 'event_categ' ] ) ) {
121
+ $events = $this->get( 'events' )->get_events_data( array( 'column' => 'event_id', 'list' => $params[ 'events' ] ) );
122
+ }
123
+
124
+ // get event by category
125
+ if ( ! empty( $params[ 'event_categ' ] ) ) {
126
+ $events_categ = $this->get( 'events' )->get_events_data_by_category( $params[ 'event_categ' ] );
127
+ }
128
+
129
+ // get event by column
130
+ if ( ! empty( $params[ 'col' ] ) && empty( $params[ 'event_categ' ] ) && empty( $params[ 'events' ] ) ) {
131
+ $events = $this->get( 'events' )->get_events_data( array( 'column' => 'column_id', 'list' => $params[ 'col' ] ) );
132
+ }
133
+
134
+ //Columns + events
135
+ if ( ! empty( $params[ 'events' ] ) && ! empty( $params[ 'col' ] ) && empty( $params[ 'event_categ' ] ) ) {
136
+ $events = $this->get( 'events' )->get_events_data( array( 'column' => array( 'column_id', 'event_id' ), 'list' => array( 'column_id' => $params[ 'col' ], 'event_id' => $params[ 'events' ] ) ) );
137
+ }
138
+
139
+ //Events + Categories
140
+ if ( ! empty( $params[ 'events' ] ) && ! empty( $params[ 'event_categ' ] ) && empty( $params[ 'col' ] ) ) {
141
+ $events = $this->get( 'events' )->get_events_data( array( 'column' => 'event_id', 'list' => $params[ 'events' ] ) );
142
+ }
143
+
144
+ //if all params empty
145
+ if ( empty( $params[ 'col' ] ) && empty( $params[ 'event_categ' ] ) && empty( $params[ 'events' ] ) ) {
146
+ $events = $this->get( 'events' )->get_events_data( array( 'column' => 'event_id', 'all' => true ) );
147
+ $events_categ = $this->get( 'events' )->get_events_data_by_category( '' );
148
+ }
149
+ // select all event option
150
+ if ( ! empty( $params[ 'col' ] ) && ! empty( $params[ 'event_categ' ] ) && ! empty( $params[ 'events' ] ) ) {
151
+ $events = $this->get( 'events' )->get_events_data( array( 'column' => 'event_id', 'list' => $params[ 'events' ] ) );
152
+ }
153
+
154
+ $events_data[ 'events' ] = array_merge( $events_data[ 'events' ], $events_categ, $events );
155
+
156
+ //Create column array;
157
+ if ( empty( $params[ 'col' ] ) ) {
158
+ foreach ( $events_data[ 'events' ] as $event ) {
159
+ $columns_ids[] = $event->column_id;
160
+ }
161
+ $columns_ids = array_unique( $columns_ids );
162
+ } else {
163
+ $columns_ids = explode( ',', $params[ 'col' ] );
164
+ }
165
+
166
+ //Sort column by menu order
167
+ $events_data[ 'column' ] = $this->get( 'column' )->get_all_column( array( 'post__in' => $columns_ids ) );
168
+
169
+ if ( ! empty( $events_data[ 'column' ] ) ) {
170
+ foreach ( $events_data[ 'column' ] as $key => $column ) {
171
+ $column_events = array();
172
+ // add to column events
173
+
174
+ foreach ( $events_data[ 'events' ] as $event_key => $event ) {
175
+ if ( $column->ID == $event->column_id ) {
176
+ $start_index = $this->get_event_index( $params[ 'increment' ], $event->event_start, $step, 'start' );
177
+ $end_index = $this->get_event_index( $params[ 'increment' ], $event->event_end, $step, 'end' );
178
+ $event->output = false;
179
+ $event->start_index = $start_index;
180
+ $event->end_index = ( $end_index < $start_index ) ? $this->get_event_index( $params[ 'increment' ], '23:59', $step, 'end' ) : $end_index;
181
+ $column_events[ $event->id ] = $event;
182
+ $events_data[ 'unique_events' ][ $event->event_id ] = $event;
183
+ }
184
+ }
185
+
186
+ //sort by start date
187
+ $column_events = $this->get_model( 'events' )->sort_by_param( $column_events );
188
+
189
+ $events_data[ 'column_events' ][ $column->ID ] = $column_events;
190
+ }
191
+ } else {
192
+ $events_data[ 'events' ] = array();
193
+ }
194
+
195
+ return $events_data;
196
+ }
197
+
198
+ /**
199
+ * @param $increment
200
+ * @param $time
201
+ * @param $step
202
+ * @param $type
203
+ *
204
+ * @return float|int
205
+ */
206
+ protected function get_event_index( $increment, $time, $step, $type ) {
207
+ if ( $type == 'start' ) {
208
+ $index = date( 'G', strtotime( $time ) ) / $increment + floor( date( 'i', strtotime( $time ) ) / $step );
209
+ } else {
210
+ $index = date( 'G', strtotime( $time ) ) / $increment + ceil( date( 'i', strtotime( $time ) ) / $step );
211
+ }
212
+
213
+ return $index;
214
+ }
215
+
216
+ /**
217
+ * Get events Id
218
+ *
219
+ * @param array $data
220
+ *
221
+ * @return string
222
+ */
223
+ public function get_event_ids( array $data = array() ) {
224
+ $ids_list = array();
225
+ if ( empty( $data ) ) {
226
+ return '';
227
+ } else {
228
+ foreach ( $data as $event ) {
229
+ $ids_list[] = $event->id;
230
+ }
231
+
232
+ return implode( ',', $ids_list );
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Integration in motopress
238
+ *
239
+ * @param $motopressCELibrary
240
+ */
241
+ public function integration_motopress( $motopressCELibrary ) {
242
+ $columns = $this->create_list_motopress( Column::get_instance()->get_all_column() );
243
+ $events = $this->create_list_motopress( Events::get_instance()->get_all_events() );
244
+
245
+ $categories = get_terms( 'mp-event_category', 'orderby=count&hide_empty=0' );
246
+ $categories = $this->create_list_motopress( $categories, 'term' );
247
+
248
+ $attributes = array(
249
+ 'col' => array(
250
+ 'type' => 'select-multiple',
251
+ 'label' => __( 'Column', 'mp-timetable' ),
252
+ 'list' => $columns
253
+ ),
254
+ 'events' => array(
255
+ 'type' => 'select-multiple',
256
+ 'label' => __( 'Events', 'mp-timetable' ),
257
+ 'list' => $events
258
+ ),
259
+ 'event_categ' => array(
260
+ 'type' => 'select-multiple',
261
+ 'label' => __( 'Event categories', 'mp-timetable' ),
262
+ 'list' => $categories
263
+ ),
264
+ 'increment' => array(
265
+ 'type' => 'select',
266
+ 'label' => __( 'Hour measure', 'mp-timetable' ),
267
+ 'list' => array( '1' => __( 'Hour (1h)', 'mp-timetable' ), '0.5' => __( 'Half hour (30min)', 'mp-timetable' ), '0.25' => __( 'Quarter hour (15min)', 'mp-timetable' ) )
268
+ ),
269
+ 'view' => array(
270
+ 'type' => 'select',
271
+ 'label' => __( 'Filter style', 'mp-timetable' ),
272
+ 'list' => array( 'dropdown_list' => __( 'Dropdown list', 'mp-timetable' ), 'tabs' => __( 'Tabs', 'mp-timetable' ) )
273
+ ),
274
+ 'label' => array(
275
+ 'type' => 'text',
276
+ 'label' => __( 'Filter label', 'mp-timetable' ),
277
+ 'default' => __( 'All Events', 'mp-timetable' )
278
+ ),
279
+ 'hide_label' => array(
280
+ 'type' => 'select',
281
+ 'label' => __( "Hide 'All Events' view", 'mp-timetable' ),
282
+ 'list' => array( '0' => __( 'No', 'mp-timetable' ), '1' => __( 'Yes', 'mp-timetable' ) )
283
+ ),
284
+ 'hide_hrs' => array(
285
+ 'type' => 'select',
286
+ 'label' => __( 'Hide first (hours) column', 'mp-timetable' ),
287
+ 'list' => array( '0' => __( 'No', 'mp-timetable' ), '1' => __( 'Yes', 'mp-timetable' ) )
288
+ ),
289
+ 'hide_empty_rows' => array(
290
+ 'type' => 'select',
291
+ 'label' => __( 'Hide empty rows', 'mp-timetable' ),
292
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
293
+ 'default' => 1
294
+ ),
295
+ 'title' => array(
296
+ 'type' => 'radio-buttons',
297
+ 'label' => __( 'Title', 'mp-timetable' ),
298
+ 'default' => 1,
299
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
300
+ ),
301
+ 'time' => array(
302
+ 'type' => 'radio-buttons',
303
+ 'label' => __( 'Time', 'mp-timetable' ),
304
+ 'default' => 1,
305
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
306
+ ),
307
+ 'sub-title' => array(
308
+ 'type' => 'radio-buttons',
309
+ 'label' => __( 'Subtitle', 'mp-timetable' ),
310
+ 'default' => 1,
311
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
312
+ ),
313
+ 'description' => array(
314
+ 'type' => 'radio-buttons',
315
+ 'label' => __( 'Description', 'mp-timetable' ),
316
+ 'default' => 0,
317
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
318
+ ),
319
+ 'user' => array(
320
+ 'type' => 'radio-buttons',
321
+ 'label' => __( 'User', 'mp-timetable' ),
322
+ 'default' => 0,
323
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
324
+ ),
325
+ 'disable_event_url' => array(
326
+ 'type' => 'select',
327
+ 'label' => __( 'Disable event URL', 'mp-timetable' ),
328
+ 'list' => array( '0' => __( 'No', 'mp-timetable' ), '1' => __( 'Yes', 'mp-timetable' ) )
329
+ ),
330
+ 'text_align' => array(
331
+ 'type' => 'select',
332
+ 'label' => __( 'Text align', 'mp-timetable' ),
333
+ 'list' => array( 'center' => __( 'center', 'mp-timetable' ), 'left' => __( 'left', 'mp-timetable' ), 'right' => __( 'right', 'mp-timetable' ) )
334
+ ),
335
+ 'id' => array(
336
+ 'type' => 'text',
337
+ 'label' => __( 'Id', 'mp-timetable' )
338
+ ),
339
+ 'row_height' => array(
340
+ 'type' => 'text',
341
+ 'label' => __( 'Row height (in px)', 'mp-timetable' ),
342
+ 'default' => 45
343
+ ),
344
+ 'font_size' => array(
345
+ 'type' => 'text',
346
+ 'label' => __( 'Base Font Size', 'mp-timetable' ),
347
+ 'default' => ''
348
+ ),
349
+ 'responsive' => array(
350
+ 'type' => 'select',
351
+ 'label' => __( 'Responsive', 'mp-timetable' ),
352
+ 'list' => array( '1' => __( 'Yes', 'mp-timetable' ), '0' => __( 'No', 'mp-timetable' ) ),
353
+ 'default' => 1,
354
+ )
355
+ );
356
+ $mp_timetable = new \MPCEObject( 'mp-timetable', __( 'Timetable', 'mp-timetable' ), '', $attributes );
357
+
358
+ $motopressCELibrary->addObject( $mp_timetable, 'other' );
359
+ }
360
+
361
+ /**
362
+ * @param array $data_array
363
+ * @param string $type
364
+ *
365
+ * @return array
366
+ */
367
+ public function create_list_motopress( $data_array = array(), $type = 'post' ) {
368
+ $list_array = array();
369
+ switch ( $type ) {
370
+ case "post":
371
+ foreach ( $data_array as $item ) {
372
+ $list_array[ $item->ID ] = $item->post_title;
373
+ }
374
+ break;
375
+ case "term":
376
+ foreach ( $data_array as $item ) {
377
+ $list_array[ $item->term_id ] = $item->name;
378
+ }
379
+ break;
380
+ default:
381
+ break;
382
+ }
383
+
384
+ return $list_array;
385
+ }
386
+ }
classes/class-state-factory.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ /**
6
+ * Singleton factory
7
+ */
8
+ class State_Factory {
9
+
10
+ protected static $instance;
11
+ protected $namespace;
12
+
13
+ /**
14
+ * State_Factory constructor.
15
+ *
16
+ * @param string $namespace
17
+ */
18
+ public function __construct($namespace = 'plugin_core') {
19
+ $this->namespace = $namespace;
20
+ }
21
+
22
+ /**
23
+ * @return State_Factory
24
+ */
25
+ public static function get_instance() {
26
+ if (null === self::$instance) {
27
+ self::$instance = new self();
28
+ }
29
+ return self::$instance;
30
+ }
31
+
32
+ /**
33
+ * Get register instance object
34
+ *
35
+ * @param null $value
36
+ *
37
+ * @return bool|\mp_timetable\plugin_core\classes\Model
38
+ */
39
+ public function get_model($value = null) {
40
+ $model = false;
41
+ if ('model' == $value) {
42
+ $model = Model::get_instance();
43
+ } else {
44
+ $class = "{$this->namespace}\classes\models\\" . ucfirst($value);
45
+ if (class_exists($class)) {
46
+ $model = $class::get_instance();
47
+ }
48
+ }
49
+ return $model;
50
+ }
51
+
52
+ /**
53
+ * Get controller instance object
54
+ *
55
+ * @param null $value
56
+ *
57
+ * @return bool
58
+ */
59
+ public function get_controller($value = null) {
60
+ $controller = false;
61
+ $class = "{$this->namespace}\classes\controllers\Controller_" . ucfirst($value);
62
+ if (class_exists($class)) {
63
+ $controller = $class::get_instance();
64
+ }
65
+ return $controller;
66
+ }
67
+
68
+ /**
69
+ * Get Preprocessor instance object
70
+ *
71
+ * @param null $value
72
+ *
73
+ * @return bool
74
+ */
75
+ public function get_preprocessor($value = null) {
76
+ $preprocessor = false;
77
+ $class = "{$this->namespace}\classes\preprocessors\Preprocessor_" . ucfirst($value);
78
+ if (class_exists($class)) {
79
+ $preprocessor = $class::get_instance();
80
+ }
81
+ return $preprocessor;
82
+ }
83
+
84
+ }
classes/class-view.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes;
4
+
5
+ use Mp_Time_Table;
6
+
7
+ /**
8
+ * View class
9
+ */
10
+ class View {
11
+
12
+ protected static $instance;
13
+ protected $template_path;
14
+ protected $templates_path;
15
+ protected $prefix = 'mptt';
16
+ private $data;
17
+
18
+ /**
19
+ * View constructor.
20
+ */
21
+ public function __construct() {
22
+ $this->template_path = Mp_Time_Table::get_template_path();
23
+ $this->templates_path = Mp_Time_Table::get_templates_path();
24
+ $this->taxonomy_names = Core::get_instance()->get_taxonomy_names();
25
+ $this->post_types = Core::get_instance()->get_post_types();
26
+ }
27
+
28
+ /**
29
+ * @return View
30
+ */
31
+ public static function get_instance() {
32
+ if (null === self::$instance) {
33
+ self::$instance = new self();
34
+ }
35
+ return self::$instance;
36
+ }
37
+
38
+ /**
39
+ * Render template
40
+ *
41
+ * @param null $template
42
+ * @param null $data
43
+ */
44
+ function render_template($template = null, $data = null) {
45
+ $this->template = $template;
46
+ if (is_array($data)) {
47
+ extract($data);
48
+ }
49
+ $this->data = $data;
50
+ include_once $this->templates_path . 'index.php';
51
+ }
52
+
53
+ /**
54
+ * Render html
55
+ *
56
+ * @param $template
57
+ * @param null $data
58
+ * @param bool $output
59
+ *
60
+ * @return string
61
+ */
62
+ public function render_html($template, $data = null, $output = true) {
63
+ $includeFile = $this->templates_path . $template . '.php';
64
+
65
+ ob_start();
66
+
67
+ if (is_array($data)) {
68
+ extract($data);
69
+ }
70
+
71
+ $this->data = $data;
72
+
73
+ include($includeFile);
74
+
75
+ $out = ob_get_clean();
76
+
77
+ if ($output) {
78
+ echo $out;
79
+ } else {
80
+ return $out;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Get template part theme/plugin
86
+ *
87
+ * @param string $name
88
+ * @param string $slug
89
+ *
90
+ * @return void
91
+ */
92
+ public function get_template_part($slug, $name = '') {
93
+ $template = '';
94
+
95
+ if ($name) {
96
+ $template = locate_template(array("{$slug}-{$name}.php", $this->template_path . "{$slug}-{$name}.php"));
97
+ }
98
+
99
+ // Get default slug-name.php
100
+ if (!$template && $name && file_exists($this->templates_path . "{$slug}-{$name}.php")) {
101
+ $template = $this->templates_path . "{$slug}-{$name}.php";
102
+ }
103
+
104
+ if (!$template) {
105
+ $template = locate_template(array("{$slug}.php", $this->template_path . "{$slug}.php"));
106
+ }
107
+
108
+ // Allow 3rd party plugins to filter template file from their plugin.
109
+ $template = apply_filters($this->prefix . '_get_template_part', $template, $slug, $name);
110
+
111
+ if ($template) {
112
+ load_template($template, false);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @param $template_name
118
+ * @param array $args
119
+ * @param string $template_path
120
+ * @param string $default_path
121
+ *
122
+ * @return mixed/void
123
+ */
124
+ public function get_template_html($template_name, $args = array(), $template_path = '', $default_path = '') {
125
+ ob_start();
126
+ $this->get_template($template_name, $args, $template_path, $default_path);
127
+ return ob_get_clean();
128
+ }
129
+
130
+ /**
131
+ * Get template
132
+ *
133
+ * @param $template_name
134
+ * @param array $args
135
+ * @param string $template_path
136
+ * @param string $default_path
137
+ */
138
+ public function get_template($template_name, $args = array(), $template_path = '', $default_path = '') {
139
+ $template_name = $template_name . '.php';
140
+
141
+ if (!empty($args) && is_array($args)) {
142
+ extract($args);
143
+ }
144
+
145
+ $located = $this->locate_template($template_name, $template_path, $default_path);
146
+
147
+ if (!file_exists($located)) {
148
+ _doing_it_wrong(__FUNCTION__, sprintf('<code>%s</code> does not exist.', $located), '2.1');
149
+ return;
150
+ }
151
+
152
+ // Allow 3rd party plugin filter template file from their plugin.
153
+ $located = apply_filters($this->prefix . '_get_template', $located, $template_name, $args, $template_path, $default_path);
154
+
155
+ do_action($this->prefix . '_before_template_part', $template_name, $template_path, $located, $args);
156
+
157
+ include($located);
158
+
159
+ do_action($this->prefix . '_after_template_part', $template_name, $template_path, $located, $args);
160
+ }
161
+
162
+ /**
163
+ * Locate template
164
+ *
165
+ * @param $template_name
166
+ * @param string $template_path
167
+ * @param string $default_path
168
+ *
169
+ * @return mixed|void
170
+ */
171
+ function locate_template($template_name, $template_path = '', $default_path = '') {
172
+ if (!$template_path) {
173
+ $template_path = $this->template_path;
174
+ }
175
+
176
+ if (!$default_path) {
177
+ $default_path = $this->templates_path;
178
+ }
179
+
180
+ // Look within passed path within the theme - this is priority.
181
+ $template_args = array(trailingslashit($template_path) . $template_name, $template_name);
182
+
183
+ $template = locate_template($template_args);
184
+
185
+ // Get default template/
186
+ if (!$template) {
187
+ $template = $default_path . $template_name;
188
+ }
189
+
190
+ // Return what we found.
191
+ return apply_filters($this->prefix . '_locate_template', $template, $template_name, $template_path);
192
+ }
193
+
194
+
195
+ /**
196
+ * Include template
197
+ *
198
+ * @param $template
199
+ *
200
+ * @return string
201
+ */
202
+ public function template_loader($template) {
203
+ global $post, $taxonomy;
204
+ $file = '';
205
+ $find = array();
206
+ if (is_embed()) {
207
+ return $template;
208
+ }
209
+ if (is_single() && in_array($post->post_type, $this->post_types)) {
210
+ $file = "single-{$post->post_type}.php";
211
+ $find[] = $file;
212
+ $find[] = $this->template_path . $file;
213
+ } elseif (in_array($taxonomy, $this->taxonomy_names)) {
214
+ $term = get_queried_object();
215
+ $file = "taxonomy-{$term->taxonomy}.php";
216
+ $find[] = 'taxonomy-' . $term->taxonomy . '-' . $term->slug . '.php';
217
+ $find[] = $this->template_path . 'taxonomy-' . $term->taxonomy . '-' . $term->slug . '.php';
218
+ $find[] = 'taxonomy-' . $term->taxonomy . '.php';
219
+ $find[] = $this->template_path . 'taxonomy-' . $term->taxonomy . '.php';
220
+ $find[] = $file;
221
+ $find[] = $this->template_path . $file;
222
+ }
223
+ // elseif (is_post_type_archive($this->post_types)) {
224
+ // $file = 'archive.php';
225
+ // $find[] = $file;
226
+ // $find[] = $this->template_path . $file;
227
+ // }
228
+ if ($file) {
229
+ $find_template = locate_template(array_unique($find));
230
+ if (!empty($find_template)) {
231
+ $template = $find_template;
232
+ }
233
+ }
234
+
235
+ return $template;
236
+ }
237
+ }
classes/controllers/class-controller-column.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\controllers;
4
+
5
+ use mp_timetable\plugin_core\classes\Controller as Controller;
6
+
7
+ /**
8
+ * Created by PhpStorm.
9
+ * User: newmind
10
+ * Date: 12/9/2015
11
+ * Time: 5:34 PM
12
+ */
13
+ class Controller_Column extends Controller {
14
+
15
+ protected static $instance;
16
+ private $data;
17
+
18
+ public static function get_instance() {
19
+ if (null === self::$instance) {
20
+ self::$instance = new self();
21
+ }
22
+ return self::$instance;
23
+ }
24
+
25
+ /**
26
+ * Action template
27
+ */
28
+ public function action_template() {
29
+ $this->data = $_REQUEST;
30
+ $this->get_view()->render_html("events/index", $this->data);
31
+ }
32
+
33
+ /**
34
+ * @param $post
35
+ */
36
+ public function action_page_view($post) {
37
+ $events = $this->get('events')->get_event_data(array('field' => 'column_id', 'id' => $post->ID));
38
+ return $events;
39
+ }
40
+
41
+ }
classes/controllers/class-controller-events.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\controllers;
4
+
5
+ use mp_timetable\plugin_core\classes\Controller as Controller;
6
+
7
+ /**
8
+ * Created by PhpStorm.
9
+ * User: newmind
10
+ * Date: 12/9/2015
11
+ * Time: 5:34 PM
12
+ */
13
+ class Controller_Events extends Controller {
14
+
15
+ protected static $instance;
16
+ private $data;
17
+
18
+ /**
19
+ * @return Controller_Events
20
+ */
21
+ public static function get_instance() {
22
+ if (null === self::$instance) {
23
+ self::$instance = new self();
24
+ }
25
+
26
+ return self::$instance;
27
+ }
28
+
29
+ /**
30
+ * Action template
31
+ */
32
+ public function action_template() {
33
+ $this->data = $_REQUEST;
34
+ $this->get_view()->render_html("events/index", $this->data);
35
+ }
36
+
37
+ /**
38
+ * Delete event data by ID
39
+ */
40
+ public function action_delete() {
41
+ $result = $this->get('events')->delete_event(filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT));
42
+ if ($result === false) {
43
+ wp_send_json_error(array('status' => $result));
44
+ } else {
45
+ wp_send_json_success(array('status' => $result));
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Get single event data
51
+ */
52
+ public function action_get_event_data() {
53
+ $id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
54
+ $result = $this->get('events')->get_event_data(array('field' => 'id', 'id' => $id), 'event_start', false);
55
+ if (!empty($result)) {
56
+ wp_send_json_success($result[ 0 ]);
57
+ } else {
58
+ wp_send_json_error(array('status' => false));
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get events by column id
64
+ *
65
+ * @param $post
66
+ *
67
+ * @return mixed
68
+ */
69
+ public function get_all_event_by_post($post) {
70
+ // Show draft timeslots on preview
71
+ $show_public_only = ((get_post_status($post->ID) == 'draft') && is_preview()) ? false : true;
72
+
73
+ $result = $this->get('events')->get_event_data(array('field' => 'event_id', 'id' => $post->ID), 'event_start', $show_public_only);
74
+
75
+ return $result;
76
+ }
77
+
78
+ /**
79
+ * Update Single Event data
80
+ */
81
+ public function action_update_event_data() {
82
+ $request = $_REQUEST;
83
+ $result = $this->get('events')->update_event_data($request[ 'data' ]);
84
+ if ($result === false) {
85
+ wp_send_json_error(array('status' => false));
86
+ } else {
87
+ wp_send_json_success(array('data' => $result));
88
+ }
89
+ }
90
+ }
classes/controllers/class-controller-help.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\controllers;
4
+
5
+ use mp_timetable\plugin_core\classes\Controller as Controller;
6
+ use mp_timetable\plugin_core\classes\View;
7
+
8
+ /**
9
+ * Class Controller_Help
10
+ * @package mp_timetable\classes\controllers
11
+ */
12
+ class Controller_Help extends Controller {
13
+
14
+ protected static $instance;
15
+
16
+ public static function get_instance() {
17
+ if (null === self::$instance) {
18
+ self::$instance = new self();
19
+ }
20
+ return self::$instance;
21
+ }
22
+
23
+ /**
24
+ * Action template
25
+ */
26
+ public function action_content() {
27
+ View::get_instance()->render_html('../admin/help/index');
28
+ }
29
+ }
classes/controllers/class-controller-import.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\controllers;
4
+
5
+ use mp_timetable\classes\models\Export;
6
+ use mp_timetable\plugin_core\classes\Controller as Controller;
7
+ use mp_timetable\plugin_core\classes\View;
8
+
9
+ /**
10
+ * Class Controller_Import
11
+ * @package mp_timetable\classes\controllers
12
+ */
13
+ class Controller_Import extends Controller {
14
+
15
+ protected static $instance;
16
+
17
+ public static function get_instance() {
18
+ if (null === self::$instance) {
19
+ self::$instance = new self();
20
+ }
21
+ return self::$instance;
22
+ }
23
+
24
+ /**
25
+ * Action template
26
+ */
27
+ public function action_content() {
28
+ $data = array();
29
+ View::get_instance()->render_html('../admin/import/index', $data);
30
+ }
31
+
32
+ public function action_export() {
33
+ Export::get_instance()->export();
34
+ }
35
+ }
classes/controllers/class-controller-popup.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\controllers;
4
+
5
+ use mp_timetable\plugin_core\classes\Controller as Controller;
6
+ use mp_timetable\plugin_core\classes\Model as Model;
7
+
8
+ /**
9
+ * Class Controller_Popup
10
+ * @package mp_timetable\classes\controllers
11
+ */
12
+ class Controller_Popup extends Controller {
13
+
14
+ protected static $instance;
15
+ private $data;
16
+
17
+ public static function get_instance() {
18
+ if (null === self::$instance) {
19
+ self::$instance = new self();
20
+ }
21
+ return self::$instance;
22
+ }
23
+
24
+ /**
25
+ * Action template
26
+ */
27
+ public function action_get_popup_html_content() {
28
+ $this->data['column'] = $this->get('column')->get_all_column();
29
+ $this->data['events'] = $this->get('events')->get_all_events();
30
+ $this->data['category'] = get_terms('mp-event_category', 'orderby=count&hide_empty=0');
31
+ $data["html"] = $this->get_view()->render_html("popup/index", $this->data, false);
32
+ $this->send_json(Model::get_instance()->get_arr($data, true));
33
+ }
34
+
35
+
36
+ }
classes/controllers/class-controller-settings.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\controllers;
4
+
5
+ use mp_timetable\classes\models\Settings;
6
+ use mp_timetable\plugin_core\classes\Controller as Controller;
7
+ use mp_timetable\plugin_core\classes\View;
8
+
9
+ /**
10
+ * Class Controller_Settings
11
+ * @package mp_timetable\classes\controllers
12
+ */
13
+ class Controller_Settings extends Controller {
14
+
15
+ protected static $instance;
16
+
17
+ /**
18
+ * @return Controller_Settings
19
+ */
20
+ public static function get_instance() {
21
+ if (null === self::$instance) {
22
+ self::$instance = new self();
23
+ }
24
+ return self::$instance;
25
+ }
26
+
27
+ /**
28
+ * Action template
29
+ */
30
+ public function action_content() {
31
+ $data = Settings::get_instance()->get_settings();
32
+ $theme_supports = $this->get('Settings')->is_theme_supports();
33
+
34
+ View::get_instance()->render_html('../templates/settings/general', array('settings' => $data, 'theme_supports' => $theme_supports));
35
+ }
36
+
37
+ /**
38
+ * Save settings
39
+ */
40
+ public function action_save() {
41
+ $redirect = Settings::get_instance()->save_settings();
42
+
43
+ if ($redirect) {
44
+ wp_redirect(add_query_arg(array('page' => $_GET['page'], 'settings-updated' => 'true')));
45
+ die();
46
+ }
47
+
48
+ /**
49
+ * Show success message
50
+ */
51
+ if (isset($_GET['settings-updated']) && ($_GET['settings-updated'] == TRUE)) {
52
+ $_GET['settings-updated'] = false;
53
+ add_settings_error('mpTimetableSettings', esc_attr('settings_updated'), __('Settings saved.', 'mp-timetable'), 'updated');
54
+ }
55
+ }
56
+ }
classes/libs/FirePHPCore/FirePHP.class.php ADDED
@@ -0,0 +1,1785 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\libs\FirePHPCore;
3
+ /**
4
+ * *** BEGIN LICENSE BLOCK *****
5
+ *
6
+ * This file is part of FirePHP (http://www.firephp.org/).
7
+ *
8
+ * Software License Agreement (New BSD License)
9
+ *
10
+ * Copyright (c) 2006-2010, Christoph Dorn
11
+ * All rights reserved.
12
+ *
13
+ * Redistribution and use in source and binary forms, with or without modification,
14
+ * are permitted provided that the following conditions are met:
15
+ *
16
+ * * Redistributions of source code must retain the above copyright notice,
17
+ * this list of conditions and the following disclaimer.
18
+ *
19
+ * * Redistributions in binary form must reproduce the above copyright notice,
20
+ * this list of conditions and the following disclaimer in the documentation
21
+ * and/or other materials provided with the distribution.
22
+ *
23
+ * * Neither the name of Christoph Dorn nor the names of its
24
+ * contributors may be used to endorse or promote products derived from this
25
+ * software without specific prior written permission.
26
+ *
27
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ *
38
+ * ***** END LICENSE BLOCK *****
39
+ *
40
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
41
+ * @author Christoph Dorn <christoph@christophdorn.com>
42
+ * @license http://www.opensource.org/licenses/bsd-license.php
43
+ * @package FirePHPCore
44
+ */
45
+
46
+ /**
47
+ * @see http://code.google.com/p/firephp/issues/detail?id=112
48
+ */
49
+ if (!defined('E_STRICT')) {
50
+ define('E_STRICT', 2048);
51
+ }
52
+ if (!defined('E_RECOVERABLE_ERROR')) {
53
+ define('E_RECOVERABLE_ERROR', 4096);
54
+ }
55
+ if (!defined('E_DEPRECATED')) {
56
+ define('E_DEPRECATED', 8192);
57
+ }
58
+ if (!defined('E_USER_DEPRECATED')) {
59
+ define('E_USER_DEPRECATED', 16384);
60
+ }
61
+
62
+ /**
63
+ * Sends the given data to the FirePHP Firefox Extension.
64
+ * The data can be displayed in the Firebug Console or in the
65
+ * "Server" request tab.
66
+ *
67
+ * For more information see: http://www.firephp.org/
68
+ *
69
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
70
+ * @author Christoph Dorn <christoph@christophdorn.com>
71
+ * @license http://www.opensource.org/licenses/bsd-license.php
72
+ * @package FirePHPCore
73
+ */
74
+ class FirePHP {
75
+
76
+ /**
77
+ * FirePHP version
78
+ *
79
+ * @var string
80
+ */
81
+ const VERSION = '0.3'; // @pinf replace '0.3' with '%%package.version%%'
82
+
83
+ /**
84
+ * Firebug LOG level
85
+ *
86
+ * Logs a message to firebug console.
87
+ *
88
+ * @var string
89
+ */
90
+ const LOG = 'LOG';
91
+
92
+ /**
93
+ * Firebug INFO level
94
+ *
95
+ * Logs a message to firebug console and displays an info icon before the message.
96
+ *
97
+ * @var string
98
+ */
99
+ const INFO = 'INFO';
100
+
101
+ /**
102
+ * Firebug WARN level
103
+ *
104
+ * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise.
105
+ *
106
+ * @var string
107
+ */
108
+ const WARN = 'WARN';
109
+
110
+ /**
111
+ * Firebug ERROR level
112
+ *
113
+ * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
114
+ *
115
+ * @var string
116
+ */
117
+ const ERROR = 'ERROR';
118
+
119
+ /**
120
+ * Dumps a variable to firebug's server panel
121
+ *
122
+ * @var string
123
+ */
124
+ const DUMP = 'DUMP';
125
+
126
+ /**
127
+ * Displays a stack trace in firebug console
128
+ *
129
+ * @var string
130
+ */
131
+ const TRACE = 'TRACE';
132
+
133
+ /**
134
+ * Displays an exception in firebug console
135
+ *
136
+ * Increments the firebug error count.
137
+ *
138
+ * @var string
139
+ */
140
+ const EXCEPTION = 'EXCEPTION';
141
+
142
+ /**
143
+ * Displays an table in firebug console
144
+ *
145
+ * @var string
146
+ */
147
+ const TABLE = 'TABLE';
148
+
149
+ /**
150
+ * Starts a group in firebug console
151
+ *
152
+ * @var string
153
+ */
154
+ const GROUP_START = 'GROUP_START';
155
+
156
+ /**
157
+ * Ends a group in firebug console
158
+ *
159
+ * @var string
160
+ */
161
+ const GROUP_END = 'GROUP_END';
162
+
163
+ /**
164
+ * Singleton instance of FirePHP
165
+ *
166
+ * @var FirePHP
167
+ */
168
+ protected static $instance = null;
169
+
170
+ /**
171
+ * Flag whether we are logging from within the exception handler
172
+ *
173
+ * @var boolean
174
+ */
175
+ protected $inExceptionHandler = false;
176
+
177
+ /**
178
+ * Flag whether to throw PHP errors that have been converted to ErrorExceptions
179
+ *
180
+ * @var boolean
181
+ */
182
+ protected $throwErrorExceptions = true;
183
+
184
+ /**
185
+ * Flag whether to convert PHP assertion errors to Exceptions
186
+ *
187
+ * @var boolean
188
+ */
189
+ protected $convertAssertionErrorsToExceptions = true;
190
+
191
+ /**
192
+ * Flag whether to throw PHP assertion errors that have been converted to Exceptions
193
+ *
194
+ * @var boolean
195
+ */
196
+ protected $throwAssertionExceptions = false;
197
+
198
+ /**
199
+ * Wildfire protocol message index
200
+ *
201
+ * @var int
202
+ */
203
+ protected $messageIndex = 1;
204
+
205
+ /**
206
+ * Options for the library
207
+ *
208
+ * @var array
209
+ */
210
+ protected $options = array('maxDepth' => 10,
211
+ 'maxObjectDepth' => 5,
212
+ 'maxArrayDepth' => 5,
213
+ 'useNativeJsonEncode' => true,
214
+ 'includeLineNumbers' => true);
215
+
216
+ /**
217
+ * Filters used to exclude object members when encoding
218
+ *
219
+ * @var array
220
+ */
221
+ protected $objectFilters = array(
222
+ 'firephp' => array('objectStack', 'instance', 'json_objectStack'),
223
+ 'firephp_test_class' => array('objectStack', 'instance', 'json_objectStack')
224
+ );
225
+
226
+ /**
227
+ * A stack of objects used to detect recursion during object encoding
228
+ *
229
+ * @var object
230
+ */
231
+ protected $objectStack = array();
232
+
233
+ /**
234
+ * Flag to enable/disable logging
235
+ *
236
+ * @var boolean
237
+ */
238
+ protected $enabled = true;
239
+
240
+ /**
241
+ * The insight console to log to if applicable
242
+ *
243
+ * @var object
244
+ */
245
+ protected $logToInsightConsole = null;
246
+
247
+ /**
248
+ * When the object gets serialized only include specific object members.
249
+ *
250
+ * @return array
251
+ */
252
+ public function __sleep()
253
+ {
254
+ return array('options','objectFilters','enabled');
255
+ }
256
+
257
+ /**
258
+ * Gets singleton instance of FirePHP
259
+ *
260
+ * @param boolean $AutoCreate
261
+ * @return FirePHP
262
+ */
263
+ public static function getInstance($AutoCreate = false)
264
+ {
265
+ if ($AutoCreate===true && !self::$instance) {
266
+ self::init();
267
+ }
268
+ return self::$instance;
269
+ }
270
+
271
+ /**
272
+ * Creates FirePHP object and stores it for singleton access
273
+ *
274
+ * @return FirePHP
275
+ */
276
+ public static function init()
277
+ {
278
+ return self::setInstance(new self());
279
+ }
280
+
281
+ /**
282
+ * Set the instance of the FirePHP singleton
283
+ *
284
+ * @param FirePHP $instance The FirePHP object instance
285
+ * @return FirePHP
286
+ */
287
+ public static function setInstance($instance)
288
+ {
289
+ return self::$instance = $instance;
290
+ }
291
+
292
+ /**
293
+ * Set an Insight console to direct all logging calls to
294
+ *
295
+ * @param object $console The console object to log to
296
+ * @return void
297
+ */
298
+ public function setLogToInsightConsole($console)
299
+ {
300
+ if(is_string($console)) {
301
+ if(get_class($this)!='FirePHP_Insight' && !is_subclass_of($this, 'FirePHP_Insight')) {
302
+ throw new Exception('FirePHP instance not an instance or subclass of FirePHP_Insight!');
303
+ }
304
+ $this->logToInsightConsole = $this->to('request')->console($console);
305
+ } else {
306
+ $this->logToInsightConsole = $console;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Enable and disable logging to Firebug
312
+ *
313
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
314
+ * @return void
315
+ */
316
+ public function setEnabled($Enabled)
317
+ {
318
+ $this->enabled = $Enabled;
319
+ }
320
+
321
+ /**
322
+ * Check if logging is enabled
323
+ *
324
+ * @return boolean TRUE if enabled
325
+ */
326
+ public function getEnabled()
327
+ {
328
+ return $this->enabled;
329
+ }
330
+
331
+ /**
332
+ * Specify a filter to be used when encoding an object
333
+ *
334
+ * Filters are used to exclude object members.
335
+ *
336
+ * @param string $Class The class name of the object
337
+ * @param array $Filter An array of members to exclude
338
+ * @return void
339
+ */
340
+ public function setObjectFilter($Class, $Filter)
341
+ {
342
+ $this->objectFilters[strtolower($Class)] = $Filter;
343
+ }
344
+
345
+ /**
346
+ * Set some options for the library
347
+ *
348
+ * Options:
349
+ * - maxDepth: The maximum depth to traverse (default: 10)
350
+ * - maxObjectDepth: The maximum depth to traverse objects (default: 5)
351
+ * - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
352
+ * - useNativeJsonEncode: If true will use json_encode() (default: true)
353
+ * - includeLineNumbers: If true will include line numbers and filenames (default: true)
354
+ *
355
+ * @param array $Options The options to be set
356
+ * @return void
357
+ */
358
+ public function setOptions($Options)
359
+ {
360
+ $this->options = array_merge($this->options,$Options);
361
+ }
362
+
363
+ /**
364
+ * Get options from the library
365
+ *
366
+ * @return array The currently set options
367
+ */
368
+ public function getOptions()
369
+ {
370
+ return $this->options;
371
+ }
372
+
373
+ /**
374
+ * Set an option for the library
375
+ *
376
+ * @param string $Name
377
+ * @param mixed $Value
378
+ * @throws Exception
379
+ * @return void
380
+ */
381
+ public function setOption($Name, $Value)
382
+ {
383
+ if (!isset($this->options[$Name])) {
384
+ throw $this->newException('Unknown option: ' . $Name);
385
+ }
386
+ $this->options[$Name] = $Value;
387
+ }
388
+
389
+ /**
390
+ * Get an option from the library
391
+ *
392
+ * @param string $Name
393
+ * @throws Exception
394
+ * @return mixed
395
+ */
396
+ public function getOption($Name)
397
+ {
398
+ if (!isset($this->options[$Name])) {
399
+ throw $this->newException('Unknown option: ' . $Name);
400
+ }
401
+ return $this->options[$Name];
402
+ }
403
+
404
+ /**
405
+ * Register FirePHP as your error handler
406
+ *
407
+ * Will throw exceptions for each php error.
408
+ *
409
+ * @return mixed Returns a string containing the previously defined error handler (if any)
410
+ */
411
+ public function registerErrorHandler($throwErrorExceptions = false)
412
+ {
413
+ //NOTE: The following errors will not be caught by this error handler:
414
+ // E_ERROR, E_PARSE, E_CORE_ERROR,
415
+ // E_CORE_WARNING, E_COMPILE_ERROR,
416
+ // E_COMPILE_WARNING, E_STRICT
417
+
418
+ $this->throwErrorExceptions = $throwErrorExceptions;
419
+
420
+ return set_error_handler(array($this,'errorHandler'));
421
+ }
422
+
423
+ /**
424
+ * FirePHP's error handler
425
+ *
426
+ * Throws exception for each php error that will occur.
427
+ *
428
+ * @param int $errno
429
+ * @param string $errstr
430
+ * @param string $errfile
431
+ * @param int $errline
432
+ * @param array $errcontext
433
+ */
434
+ public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
435
+ {
436
+ // Don't throw exception if error reporting is switched off
437
+ if (error_reporting() == 0) {
438
+ return;
439
+ }
440
+ // Only throw exceptions for errors we are asking for
441
+ if (error_reporting() & $errno) {
442
+
443
+ $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
444
+ if ($this->throwErrorExceptions) {
445
+ throw $exception;
446
+ } else {
447
+ $this->fb($exception);
448
+ }
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Register FirePHP as your exception handler
454
+ *
455
+ * @return mixed Returns the name of the previously defined exception handler,
456
+ * or NULL on error.
457
+ * If no previous handler was defined, NULL is also returned.
458
+ */
459
+ public function registerExceptionHandler()
460
+ {
461
+ return set_exception_handler(array($this,'exceptionHandler'));
462
+ }
463
+
464
+ /**
465
+ * FirePHP's exception handler
466
+ *
467
+ * Logs all exceptions to your firebug console and then stops the script.
468
+ *
469
+ * @param Exception $Exception
470
+ * @throws Exception
471
+ */
472
+ function exceptionHandler($Exception)
473
+ {
474
+
475
+ $this->inExceptionHandler = true;
476
+
477
+ header('HTTP/1.1 500 Internal Server Error');
478
+
479
+ try {
480
+ $this->fb($Exception);
481
+ } catch (Exception $e) {
482
+ echo 'We had an exception: ' . $e;
483
+ }
484
+ $this->inExceptionHandler = false;
485
+ }
486
+
487
+ /**
488
+ * Register FirePHP driver as your assert callback
489
+ *
490
+ * @param boolean $convertAssertionErrorsToExceptions
491
+ * @param boolean $throwAssertionExceptions
492
+ * @return mixed Returns the original setting or FALSE on errors
493
+ */
494
+ public function registerAssertionHandler($convertAssertionErrorsToExceptions = true, $throwAssertionExceptions = false)
495
+ {
496
+ $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions;
497
+ $this->throwAssertionExceptions = $throwAssertionExceptions;
498
+
499
+ if ($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) {
500
+ throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!');
501
+ }
502
+
503
+ return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
504
+ }
505
+
506
+ /**
507
+ * FirePHP's assertion handler
508
+ *
509
+ * Logs all assertions to your firebug console and then stops the script.
510
+ *
511
+ * @param string $file File source of assertion
512
+ * @param int $line Line source of assertion
513
+ * @param mixed $code Assertion code
514
+ */
515
+ public function assertionHandler($file, $line, $code)
516
+ {
517
+ if ($this->convertAssertionErrorsToExceptions) {
518
+
519
+ $exception = new ErrorException('Assertion Failed - Code[ '.$code.' ]', 0, null, $file, $line);
520
+
521
+ if ($this->throwAssertionExceptions) {
522
+ throw $exception;
523
+ } else {
524
+ $this->fb($exception);
525
+ }
526
+
527
+ } else {
528
+ $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File'=>$file,'Line'=>$line));
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Start a group for following messages.
534
+ *
535
+ * Options:
536
+ * Collapsed: [true|false]
537
+ * Color: [#RRGGBB|ColorName]
538
+ *
539
+ * @param string $Name
540
+ * @param array $Options OPTIONAL Instructions on how to log the group
541
+ * @return true
542
+ * @throws Exception
543
+ */
544
+ public function group($Name, $Options = null)
545
+ {
546
+
547
+ if (!$Name) {
548
+ throw $this->newException('You must specify a label for the group!');
549
+ }
550
+
551
+ if ($Options) {
552
+ if (!is_array($Options)) {
553
+ throw $this->newException('Options must be defined as an array!');
554
+ }
555
+ if (array_key_exists('Collapsed', $Options)) {
556
+ $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false';
557
+ }
558
+ }
559
+
560
+ return $this->fb(null, $Name, FirePHP::GROUP_START, $Options);
561
+ }
562
+
563
+ /**
564
+ * Ends a group you have started before
565
+ *
566
+ * @return true
567
+ * @throws Exception
568
+ */
569
+ public function groupEnd()
570
+ {
571
+ return $this->fb(null, null, FirePHP::GROUP_END);
572
+ }
573
+
574
+ /**
575
+ * Log object with label to firebug console
576
+ *
577
+ * @see FirePHP::LOG
578
+ * @param mixes $Object
579
+ * @param string $Label
580
+ * @return true
581
+ * @throws Exception
582
+ */
583
+ public function log($Object, $Label = null, $Options = array())
584
+ {
585
+ return $this->fb($Object, $Label, FirePHP::LOG, $Options);
586
+ }
587
+
588
+ /**
589
+ * Log object with label to firebug console
590
+ *
591
+ * @see FirePHP::INFO
592
+ * @param mixes $Object
593
+ * @param string $Label
594
+ * @return true
595
+ * @throws Exception
596
+ */
597
+ public function info($Object, $Label = null, $Options = array())
598
+ {
599
+ return $this->fb($Object, $Label, FirePHP::INFO, $Options);
600
+ }
601
+
602
+ /**
603
+ * Log object with label to firebug console
604
+ *
605
+ * @see FirePHP::WARN
606
+ * @param mixes $Object
607
+ * @param string $Label
608
+ * @return true
609
+ * @throws Exception
610
+ */
611
+ public function warn($Object, $Label = null, $Options = array())
612
+ {
613
+ return $this->fb($Object, $Label, FirePHP::WARN, $Options);
614
+ }
615
+
616
+ /**
617
+ * Log object with label to firebug console
618
+ *
619
+ * @see FirePHP::ERROR
620
+ * @param mixes $Object
621
+ * @param string $Label
622
+ * @return true
623
+ * @throws Exception
624
+ */
625
+ public function error($Object, $Label = null, $Options = array())
626
+ {
627
+ return $this->fb($Object, $Label, FirePHP::ERROR, $Options);
628
+ }
629
+
630
+ /**
631
+ * Dumps key and variable to firebug server panel
632
+ *
633
+ * @see FirePHP::DUMP
634
+ * @param string $Key
635
+ * @param mixed $Variable
636
+ * @return true
637
+ * @throws Exception
638
+ */
639
+ public function dump($Key, $Variable, $Options = array())
640
+ {
641
+ if (!is_string($Key)) {
642
+ throw $this->newException('Key passed to dump() is not a string');
643
+ }
644
+ if (strlen($Key)>100) {
645
+ throw $this->newException('Key passed to dump() is longer than 100 characters');
646
+ }
647
+ if (!preg_match_all('/^[a-zA-Z0-9-_\.:]*$/', $Key, $m)) {
648
+ throw $this->newException('Key passed to dump() contains invalid characters [a-zA-Z0-9-_\.:]');
649
+ }
650
+ return $this->fb($Variable, $Key, FirePHP::DUMP, $Options);
651
+ }
652
+
653
+ /**
654
+ * Log a trace in the firebug console
655
+ *
656
+ * @see FirePHP::TRACE
657
+ * @param string $Label
658
+ * @return true
659
+ * @throws Exception
660
+ */
661
+ public function trace($Label)
662
+ {
663
+ return $this->fb($Label, FirePHP::TRACE);
664
+ }
665
+
666
+ /**
667
+ * Log a table in the firebug console
668
+ *
669
+ * @see FirePHP::TABLE
670
+ * @param string $Label
671
+ * @param string $Table
672
+ * @return true
673
+ * @throws Exception
674
+ */
675
+ public function table($Label, $Table, $Options = array())
676
+ {
677
+ return $this->fb($Table, $Label, FirePHP::TABLE, $Options);
678
+ }
679
+
680
+ /**
681
+ * Insight API wrapper
682
+ *
683
+ * @see Insight_Helper::to()
684
+ */
685
+ public static function to()
686
+ {
687
+ $instance = self::getInstance();
688
+ if (!method_exists($instance, "_to")) {
689
+ throw new Exception("FirePHP::to() implementation not loaded");
690
+ }
691
+ $args = func_get_args();
692
+ return call_user_func_array(array($instance, '_to'), $args);
693
+ }
694
+
695
+ /**
696
+ * Insight API wrapper
697
+ *
698
+ * @see Insight_Helper::plugin()
699
+ */
700
+ public static function plugin()
701
+ {
702
+ $instance = self::getInstance();
703
+ if (!method_exists($instance, "_plugin")) {
704
+ throw new Exception("FirePHP::plugin() implementation not loaded");
705
+ }
706
+ $args = func_get_args();
707
+ return call_user_func_array(array($instance, '_plugin'), $args);
708
+ }
709
+
710
+ /**
711
+ * Check if FirePHP is installed on client
712
+ *
713
+ * @return boolean
714
+ */
715
+ public function detectClientExtension()
716
+ {
717
+ // Check if FirePHP is installed on client via User-Agent header
718
+ if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si',$this->getUserAgent(),$m) &&
719
+ version_compare($m[1][0],'0.0.6','>=')) {
720
+ return true;
721
+ } else
722
+ // Check if FirePHP is installed on client via X-FirePHP-Version header
723
+ if (@preg_match_all('/^([\.\d]*)$/si',$this->getRequestHeader("X-FirePHP-Version"),$m) &&
724
+ version_compare($m[1][0],'0.0.6','>=')) {
725
+ return true;
726
+ }
727
+ return false;
728
+ }
729
+
730
+ /**
731
+ * Log varible to Firebug
732
+ *
733
+ * @see http://www.firephp.org/Wiki/Reference/Fb
734
+ * @param mixed $Object The variable to be logged
735
+ * @return true Return TRUE if message was added to headers, FALSE otherwise
736
+ * @throws Exception
737
+ */
738
+ public function fb($Object)
739
+ {
740
+ if($this instanceof FirePHP_Insight && method_exists($this, '_logUpgradeClientMessage')) {
741
+ if(!FirePHP_Insight::$upgradeClientMessageLogged) { // avoid infinite recursion as _logUpgradeClientMessage() logs a message
742
+ $this->_logUpgradeClientMessage();
743
+ }
744
+ }
745
+
746
+ static $insightGroupStack = array();
747
+
748
+ if (!$this->getEnabled()) {
749
+ return false;
750
+ }
751
+
752
+ if ($this->headersSent($filename, $linenum)) {
753
+ // If we are logging from within the exception handler we cannot throw another exception
754
+ if ($this->inExceptionHandler) {
755
+ // Simply echo the error out to the page
756
+ echo '<div style="border: 2px solid red; font-family: Arial; font-size: 12px; background-color: lightgray; padding: 5px;"><span style="color: red; font-weight: bold;">FirePHP ERROR:</span> Headers already sent in <b>'.$filename.'</b> on line <b>'.$linenum.'</b>. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.</div>';
757
+ } else {
758
+ throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
759
+ }
760
+ }
761
+
762
+ $Type = null;
763
+ $Label = null;
764
+ $Options = array();
765
+
766
+ if (func_num_args()==1) {
767
+ } else
768
+ if (func_num_args()==2) {
769
+ switch(func_get_arg(1)) {
770
+ case self::LOG:
771
+ case self::INFO:
772
+ case self::WARN:
773
+ case self::ERROR:
774
+ case self::DUMP:
775
+ case self::TRACE:
776
+ case self::EXCEPTION:
777
+ case self::TABLE:
778
+ case self::GROUP_START:
779
+ case self::GROUP_END:
780
+ $Type = func_get_arg(1);
781
+ break;
782
+ default:
783
+ $Label = func_get_arg(1);
784
+ break;
785
+ }
786
+ } else
787
+ if (func_num_args()==3) {
788
+ $Type = func_get_arg(2);
789
+ $Label = func_get_arg(1);
790
+ } else
791
+ if (func_num_args()==4) {
792
+ $Type = func_get_arg(2);
793
+ $Label = func_get_arg(1);
794
+ $Options = func_get_arg(3);
795
+ } else {
796
+ throw $this->newException('Wrong number of arguments to fb() function!');
797
+ }
798
+
799
+ if($this->logToInsightConsole!==null && (get_class($this)=='FirePHP_Insight' || is_subclass_of($this, 'FirePHP_Insight'))) {
800
+ $msg = $this->logToInsightConsole;
801
+ if ($Object instanceof Exception) {
802
+ $Type = self::EXCEPTION;
803
+ }
804
+ if($Label && $Type!=self::TABLE && $Type!=self::GROUP_START) {
805
+ $msg = $msg->label($Label);
806
+ }
807
+ switch($Type) {
808
+ case self::DUMP:
809
+ case self::LOG:
810
+ return $msg->log($Object);
811
+ case self::INFO:
812
+ return $msg->info($Object);
813
+ case self::WARN:
814
+ return $msg->warn($Object);
815
+ case self::ERROR:
816
+ return $msg->error($Object);
817
+ case self::TRACE:
818
+ return $msg->trace($Object);
819
+ case self::EXCEPTION:
820
+ return $this->plugin('engine')->handleException($Object, $msg);
821
+ case self::TABLE:
822
+ if (isset($Object[0]) && !is_string($Object[0]) && $Label) {
823
+ $Object = array($Label, $Object);
824
+ }
825
+ return $msg->table($Object[0], array_slice($Object[1],1), $Object[1][0]);
826
+ case self::GROUP_START:
827
+ $insightGroupStack[] = $msg->group(md5($Label))->open();
828
+ return $msg->log($Label);
829
+ case self::GROUP_END:
830
+ if(count($insightGroupStack)==0) {
831
+ throw new Error('Too many groupEnd() as opposed to group() calls!');
832
+ }
833
+ $group = array_pop($insightGroupStack);
834
+ return $group->close();
835
+ default:
836
+ return $msg->log($Object);
837
+ }
838
+ }
839
+
840
+ if (!$this->detectClientExtension()) {
841
+ return false;
842
+ }
843
+
844
+ $meta = array();
845
+ $skipFinalObjectEncode = false;
846
+
847
+ if ($Object instanceof Exception) {
848
+
849
+ $meta['file'] = $this->_escapeTraceFile($Object->getFile());
850
+ $meta['line'] = $Object->getLine();
851
+
852
+ $trace = $Object->getTrace();
853
+ if ($Object instanceof ErrorException
854
+ && isset($trace[0]['function'])
855
+ && $trace[0]['function']=='errorHandler'
856
+ && isset($trace[0]['class'])
857
+ && $trace[0]['class']=='FirePHP') {
858
+
859
+ $severity = false;
860
+ switch($Object->getSeverity()) {
861
+ case E_WARNING: $severity = 'E_WARNING'; break;
862
+ case E_NOTICE: $severity = 'E_NOTICE'; break;
863
+ case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
864
+ case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
865
+ case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
866
+ case E_STRICT: $severity = 'E_STRICT'; break;
867
+ case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
868
+ case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
869
+ case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
870
+ }
871
+
872
+ $Object = array('Class'=>get_class($Object),
873
+ 'Message'=>$severity.': '.$Object->getMessage(),
874
+ 'File'=>$this->_escapeTraceFile($Object->getFile()),
875
+ 'Line'=>$Object->getLine(),
876
+ 'Type'=>'trigger',
877
+ 'Trace'=>$this->_escapeTrace(array_splice($trace,2)));
878
+ $skipFinalObjectEncode = true;
879
+ } else {
880
+ $Object = array('Class'=>get_class($Object),
881
+ 'Message'=>$Object->getMessage(),
882
+ 'File'=>$this->_escapeTraceFile($Object->getFile()),
883
+ 'Line'=>$Object->getLine(),
884
+ 'Type'=>'throw',
885
+ 'Trace'=>$this->_escapeTrace($trace));
886
+ $skipFinalObjectEncode = true;
887
+ }
888
+ $Type = self::EXCEPTION;
889
+
890
+ } else
891
+ if ($Type==self::TRACE) {
892
+
893
+ $trace = debug_backtrace();
894
+ if (!$trace) return false;
895
+ for( $i=0 ; $i<sizeof($trace) ; $i++ ) {
896
+
897
+ if (isset($trace[$i]['class'])
898
+ && isset($trace[$i]['file'])
899
+ && ($trace[$i]['class']=='FirePHP'
900
+ || $trace[$i]['class']=='FB')
901
+ && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
902
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
903
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
904
+ } else
905
+ if (isset($trace[$i]['class'])
906
+ && isset($trace[$i+1]['file'])
907
+ && $trace[$i]['class']=='FirePHP'
908
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
909
+ /* Skip fb() */
910
+ } else
911
+ if ($trace[$i]['function']=='fb'
912
+ || $trace[$i]['function']=='trace'
913
+ || $trace[$i]['function']=='send') {
914
+
915
+ $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'',
916
+ 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'',
917
+ 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'',
918
+ 'Message'=>$trace[$i]['args'][0],
919
+ 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'',
920
+ 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'',
921
+ 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'',
922
+ 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1)));
923
+
924
+ $skipFinalObjectEncode = true;
925
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
926
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
927
+ break;
928
+ }
929
+ }
930
+
931
+ } else
932
+ if ($Type==self::TABLE) {
933
+
934
+ if (isset($Object[0]) && is_string($Object[0])) {
935
+ $Object[1] = $this->encodeTable($Object[1]);
936
+ } else {
937
+ $Object = $this->encodeTable($Object);
938
+ }
939
+
940
+ $skipFinalObjectEncode = true;
941
+
942
+ } else
943
+ if ($Type==self::GROUP_START) {
944
+
945
+ if (!$Label) {
946
+ throw $this->newException('You must specify a label for the group!');
947
+ }
948
+
949
+ } else {
950
+ if ($Type===null) {
951
+ $Type = self::LOG;
952
+ }
953
+ }
954
+
955
+ if ($this->options['includeLineNumbers']) {
956
+ if (!isset($meta['file']) || !isset($meta['line'])) {
957
+
958
+ $trace = debug_backtrace();
959
+ for( $i=0 ; $trace && $i<sizeof($trace) ; $i++ ) {
960
+
961
+ if (isset($trace[$i]['class'])
962
+ && isset($trace[$i]['file'])
963
+ && ($trace[$i]['class']=='FirePHP'
964
+ || $trace[$i]['class']=='FB')
965
+ && (substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php'
966
+ || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) {
967
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
968
+ } else
969
+ if (isset($trace[$i]['class'])
970
+ && isset($trace[$i+1]['file'])
971
+ && $trace[$i]['class']=='FirePHP'
972
+ && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') {
973
+ /* Skip fb() */
974
+ } else
975
+ if (isset($trace[$i]['file'])
976
+ && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') {
977
+ /* Skip FB::fb() */
978
+ } else {
979
+ $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'';
980
+ $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:'';
981
+ break;
982
+ }
983
+ }
984
+ }
985
+ } else {
986
+ unset($meta['file']);
987
+ unset($meta['line']);
988
+ }
989
+
990
+ $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
991
+ $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION);
992
+
993
+ $structure_index = 1;
994
+ if ($Type==self::DUMP) {
995
+ $structure_index = 2;
996
+ $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
997
+ } else {
998
+ $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
999
+ }
1000
+
1001
+ if ($Type==self::DUMP) {
1002
+ $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}';
1003
+ } else {
1004
+ $msg_meta = $Options;
1005
+ $msg_meta['Type'] = $Type;
1006
+ if ($Label!==null) {
1007
+ $msg_meta['Label'] = $Label;
1008
+ }
1009
+ if (isset($meta['file']) && !isset($msg_meta['File'])) {
1010
+ $msg_meta['File'] = $meta['file'];
1011
+ }
1012
+ if (isset($meta['line']) && !isset($msg_meta['Line'])) {
1013
+ $msg_meta['Line'] = $meta['line'];
1014
+ }
1015
+ $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']';
1016
+ }
1017
+
1018
+ $parts = explode("\n",chunk_split($msg, 5000, "\n"));
1019
+
1020
+ for( $i=0 ; $i<count($parts) ; $i++) {
1021
+
1022
+ $part = $parts[$i];
1023
+ if ($part) {
1024
+
1025
+ if (count($parts)>2) {
1026
+ // Message needs to be split into multiple parts
1027
+ $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
1028
+ (($i==0)?strlen($msg):'')
1029
+ . '|' . $part . '|'
1030
+ . (($i<count($parts)-2)?'\\':''));
1031
+ } else {
1032
+ $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
1033
+ strlen($part) . '|' . $part . '|');
1034
+ }
1035
+
1036
+ $this->messageIndex++;
1037
+
1038
+ if ($this->messageIndex > 99999) {
1039
+ throw $this->newException('Maximum number (99,999) of messages reached!');
1040
+ }
1041
+ }
1042
+ }
1043
+
1044
+ $this->setHeader('X-Wf-1-Index',$this->messageIndex-1);
1045
+
1046
+ return true;
1047
+ }
1048
+
1049
+ /**
1050
+ * Standardizes path for windows systems.
1051
+ *
1052
+ * @param string $Path
1053
+ * @return string
1054
+ */
1055
+ protected function _standardizePath($Path)
1056
+ {
1057
+ return preg_replace('/\\\\+/','/',$Path);
1058
+ }
1059
+
1060
+ /**
1061
+ * Escape trace path for windows systems
1062
+ *
1063
+ * @param array $Trace
1064
+ * @return array
1065
+ */
1066
+ protected function _escapeTrace($Trace)
1067
+ {
1068
+ if (!$Trace) return $Trace;
1069
+ for( $i=0 ; $i<sizeof($Trace) ; $i++ ) {
1070
+ if (isset($Trace[$i]['file'])) {
1071
+ $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
1072
+ }
1073
+ if (isset($Trace[$i]['args'])) {
1074
+ $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
1075
+ }
1076
+ }
1077
+ return $Trace;
1078
+ }
1079
+
1080
+ /**
1081
+ * Escape file information of trace for windows systems
1082
+ *
1083
+ * @param string $File
1084
+ * @return string
1085
+ */
1086
+ protected function _escapeTraceFile($File)
1087
+ {
1088
+ /* Check if we have a windows filepath */
1089
+ if (strpos($File,'\\')) {
1090
+ /* First strip down to single \ */
1091
+
1092
+ $file = preg_replace('/\\\\+/','\\',$File);
1093
+
1094
+ return $file;
1095
+ }
1096
+ return $File;
1097
+ }
1098
+
1099
+ /**
1100
+ * Check if headers have already been sent
1101
+ *
1102
+ * @param string $Filename
1103
+ * @param integer $Linenum
1104
+ */
1105
+ protected function headersSent(&$Filename, &$Linenum)
1106
+ {
1107
+ return headers_sent($Filename, $Linenum);
1108
+ }
1109
+
1110
+ /**
1111
+ * Send header
1112
+ *
1113
+ * @param string $Name
1114
+ * @param string $Value
1115
+ */
1116
+ protected function setHeader($Name, $Value)
1117
+ {
1118
+ return header($Name.': '.$Value);
1119
+ }
1120
+
1121
+ /**
1122
+ * Get user agent
1123
+ *
1124
+ * @return string|false
1125
+ */
1126
+ protected function getUserAgent()
1127
+ {
1128
+ if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
1129
+ return $_SERVER['HTTP_USER_AGENT'];
1130
+ }
1131
+
1132
+ /**
1133
+ * Get all request headers
1134
+ *
1135
+ * @return array
1136
+ */
1137
+ public static function getAllRequestHeaders() {
1138
+ static $_cached_headers = false;
1139
+ if($_cached_headers!==false) {
1140
+ return $_cached_headers;
1141
+ }
1142
+ $headers = array();
1143
+ if(function_exists('getallheaders')) {
1144
+ foreach( getallheaders() as $name => $value ) {
1145
+ $headers[strtolower($name)] = $value;
1146
+ }
1147
+ } else {
1148
+ foreach($_SERVER as $name => $value) {
1149
+ if(substr($name, 0, 5) == 'HTTP_') {
1150
+ $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
1151
+ }
1152
+ }
1153
+ }
1154
+ return $_cached_headers = $headers;
1155
+ }
1156
+
1157
+ /**
1158
+ * Get a request header
1159
+ *
1160
+ * @return string|false
1161
+ */
1162
+ protected function getRequestHeader($Name)
1163
+ {
1164
+ $headers = self::getAllRequestHeaders();
1165
+ if (isset($headers[strtolower($Name)])) {
1166
+ return $headers[strtolower($Name)];
1167
+ }
1168
+ return false;
1169
+ }
1170
+
1171
+ /**
1172
+ * Returns a new exception
1173
+ *
1174
+ * @param string $Message
1175
+ * @return Exception
1176
+ */
1177
+ protected function newException($Message)
1178
+ {
1179
+ return new Exception($Message);
1180
+ }
1181
+
1182
+ /**
1183
+ * Encode an object into a JSON string
1184
+ *
1185
+ * Uses PHP's jeson_encode() if available
1186
+ *
1187
+ * @param object $Object The object to be encoded
1188
+ * @return string The JSON string
1189
+ */
1190
+ public function jsonEncode($Object, $skipObjectEncode = false)
1191
+ {
1192
+ if (!$skipObjectEncode) {
1193
+ $Object = $this->encodeObject($Object);
1194
+ }
1195
+
1196
+ if (function_exists('json_encode')
1197
+ && $this->options['useNativeJsonEncode']!=false) {
1198
+
1199
+ return json_encode($Object);
1200
+ } else {
1201
+ return $this->json_encode($Object);
1202
+ }
1203
+ }
1204
+
1205
+ /**
1206
+ * Encodes a table by encoding each row and column with encodeObject()
1207
+ *
1208
+ * @param array $Table The table to be encoded
1209
+ * @return array
1210
+ */
1211
+ protected function encodeTable($Table)
1212
+ {
1213
+
1214
+ if (!$Table) return $Table;
1215
+
1216
+ $new_table = array();
1217
+ foreach($Table as $row) {
1218
+
1219
+ if (is_array($row)) {
1220
+ $new_row = array();
1221
+
1222
+ foreach($row as $item) {
1223
+ $new_row[] = $this->encodeObject($item);
1224
+ }
1225
+
1226
+ $new_table[] = $new_row;
1227
+ }
1228
+ }
1229
+
1230
+ return $new_table;
1231
+ }
1232
+
1233
+ /**
1234
+ * Encodes an object including members with
1235
+ * protected and private visibility
1236
+ *
1237
+ * @param Object $Object The object to be encoded
1238
+ * @param int $Depth The current traversal depth
1239
+ * @return array All members of the object
1240
+ */
1241
+ protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1, $MaxDepth = 1)
1242
+ {
1243
+ if ($MaxDepth > $this->options['maxDepth']) {
1244
+ return '** Max Depth ('.$this->options['maxDepth'].') **';
1245
+ }
1246
+
1247
+ $return = array();
1248
+
1249
+ if (is_resource($Object)) {
1250
+
1251
+ return '** '.(string)$Object.' **';
1252
+
1253
+ } else
1254
+ if (is_object($Object)) {
1255
+
1256
+ if ($ObjectDepth > $this->options['maxObjectDepth']) {
1257
+ return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **';
1258
+ }
1259
+
1260
+ foreach ($this->objectStack as $refVal) {
1261
+ if ($refVal === $Object) {
1262
+ return '** Recursion ('.get_class($Object).') **';
1263
+ }
1264
+ }
1265
+ array_push($this->objectStack, $Object);
1266
+
1267
+ $return['__className'] = $class = get_class($Object);
1268
+ $class_lower = strtolower($class);
1269
+
1270
+ $reflectionClass = new ReflectionClass($class);
1271
+ $properties = array();
1272
+ foreach( $reflectionClass->getProperties() as $property) {
1273
+ $properties[$property->getName()] = $property;
1274
+ }
1275
+
1276
+ $members = (array)$Object;
1277
+
1278
+ foreach( $properties as $plain_name => $property ) {
1279
+
1280
+ $name = $raw_name = $plain_name;
1281
+ if ($property->isStatic()) {
1282
+ $name = 'static:'.$name;
1283
+ }
1284
+ if ($property->isPublic()) {
1285
+ $name = 'public:'.$name;
1286
+ } else
1287
+ if ($property->isPrivate()) {
1288
+ $name = 'private:'.$name;
1289
+ $raw_name = "\0".$class."\0".$raw_name;
1290
+ } else
1291
+ if ($property->isProtected()) {
1292
+ $name = 'protected:'.$name;
1293
+ $raw_name = "\0".'*'."\0".$raw_name;
1294
+ }
1295
+
1296
+ if (!(isset($this->objectFilters[$class_lower])
1297
+ && is_array($this->objectFilters[$class_lower])
1298
+ && in_array($plain_name,$this->objectFilters[$class_lower]))) {
1299
+
1300
+ if (array_key_exists($raw_name,$members)
1301
+ && !$property->isStatic()) {
1302
+
1303
+ $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1, $MaxDepth + 1);
1304
+
1305
+ } else {
1306
+ if (method_exists($property,'setAccessible')) {
1307
+ $property->setAccessible(true);
1308
+ $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1, $MaxDepth + 1);
1309
+ } else
1310
+ if ($property->isPublic()) {
1311
+ $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1, $MaxDepth + 1);
1312
+ } else {
1313
+ $return[$name] = '** Need PHP 5.3 to get value **';
1314
+ }
1315
+ }
1316
+ } else {
1317
+ $return[$name] = '** Excluded by Filter **';
1318
+ }
1319
+ }
1320
+
1321
+ // Include all members that are not defined in the class
1322
+ // but exist in the object
1323
+ foreach( $members as $raw_name => $value ) {
1324
+
1325
+ $name = $raw_name;
1326
+
1327
+ if ($name{0} == "\0") {
1328
+ $parts = explode("\0", $name);
1329
+ $name = $parts[2];
1330
+ }
1331
+
1332
+ $plain_name = $name;
1333
+
1334
+ if (!isset($properties[$name])) {
1335
+ $name = 'undeclared:'.$name;
1336
+
1337
+ if (!(isset($this->objectFilters[$class_lower])
1338
+ && is_array($this->objectFilters[$class_lower])
1339
+ && in_array($plain_name,$this->objectFilters[$class_lower]))) {
1340
+
1341
+ $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1, $MaxDepth + 1);
1342
+ } else {
1343
+ $return[$name] = '** Excluded by Filter **';
1344
+ }
1345
+ }
1346
+ }
1347
+
1348
+ array_pop($this->objectStack);
1349
+
1350
+ } elseif (is_array($Object)) {
1351
+
1352
+ if ($ArrayDepth > $this->options['maxArrayDepth']) {
1353
+ return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **';
1354
+ }
1355
+
1356
+ foreach ($Object as $key => $val) {
1357
+
1358
+ // Encoding the $GLOBALS PHP array causes an infinite loop
1359
+ // if the recursion is not reset here as it contains
1360
+ // a reference to itself. This is the only way I have come up
1361
+ // with to stop infinite recursion in this case.
1362
+ if ($key=='GLOBALS'
1363
+ && is_array($val)
1364
+ && array_key_exists('GLOBALS',$val)) {
1365
+ $val['GLOBALS'] = '** Recursion (GLOBALS) **';
1366
+ }
1367
+
1368
+ $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1, $MaxDepth + 1);
1369
+ }
1370
+ } else {
1371
+ if (self::is_utf8($Object)) {
1372
+ return $Object;
1373
+ } else {
1374
+ return utf8_encode($Object);
1375
+ }
1376
+ }
1377
+ return $return;
1378
+ }
1379
+
1380
+ /**
1381
+ * Returns true if $string is valid UTF-8 and false otherwise.
1382
+ *
1383
+ * @param mixed $str String to be tested
1384
+ * @return boolean
1385
+ */
1386
+ protected static function is_utf8($str)
1387
+ {
1388
+ if(function_exists('mb_detect_encoding')) {
1389
+ return (mb_detect_encoding($str) == 'UTF-8');
1390
+ }
1391
+ $c=0; $b=0;
1392
+ $bits=0;
1393
+ $len=strlen($str);
1394
+ for($i=0; $i<$len; $i++){
1395
+ $c=ord($str[$i]);
1396
+ if ($c > 128){
1397
+ if (($c >= 254)) return false;
1398
+ elseif ($c >= 252) $bits=6;
1399
+ elseif ($c >= 248) $bits=5;
1400
+ elseif ($c >= 240) $bits=4;
1401
+ elseif ($c >= 224) $bits=3;
1402
+ elseif ($c >= 192) $bits=2;
1403
+ else return false;
1404
+ if (($i+$bits) > $len) return false;
1405
+ while($bits > 1){
1406
+ $i++;
1407
+ $b=ord($str[$i]);
1408
+ if ($b < 128 || $b > 191) return false;
1409
+ $bits--;
1410
+ }
1411
+ }
1412
+ }
1413
+ return true;
1414
+ }
1415
+
1416
+ /**
1417
+ * Converts to and from JSON format.
1418
+ *
1419
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
1420
+ * format. It is easy for humans to read and write. It is easy for machines
1421
+ * to parse and generate. It is based on a subset of the JavaScript
1422
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1423
+ * This feature can also be found in Python. JSON is a text format that is
1424
+ * completely language independent but uses conventions that are familiar
1425
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
1426
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
1427
+ * ideal data-interchange language.
1428
+ *
1429
+ * This package provides a simple encoder and decoder for JSON notation. It
1430
+ * is intended for use with client-side Javascript applications that make
1431
+ * use of HTTPRequest to perform server communication functions - data can
1432
+ * be encoded into JSON notation for use in a client-side javascript, or
1433
+ * decoded from incoming Javascript requests. JSON format is native to
1434
+ * Javascript, and can be directly eval()'ed with no further parsing
1435
+ * overhead
1436
+ *
1437
+ * All strings should be in ASCII or UTF-8 format!
1438
+ *
1439
+ * LICENSE: Redistribution and use in source and binary forms, with or
1440
+ * without modification, are permitted provided that the following
1441
+ * conditions are met: Redistributions of source code must retain the
1442
+ * above copyright notice, this list of conditions and the following
1443
+ * disclaimer. Redistributions in binary form must reproduce the above
1444
+ * copyright notice, this list of conditions and the following disclaimer
1445
+ * in the documentation and/or other materials provided with the
1446
+ * distribution.
1447
+ *
1448
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1449
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1450
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
1451
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1452
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1453
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1454
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1455
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1456
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1457
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1458
+ * DAMAGE.
1459
+ *
1460
+ * @category
1461
+ * @package Services_JSON
1462
+ * @author Michal Migurski <mike-json@teczno.com>
1463
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
1464
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
1465
+ * @author Christoph Dorn <christoph@christophdorn.com>
1466
+ * @copyright 2005 Michal Migurski
1467
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
1468
+ * @license http://www.opensource.org/licenses/bsd-license.php
1469
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
1470
+ */
1471
+
1472
+
1473
+ /**
1474
+ * Keep a list of objects as we descend into the array so we can detect recursion.
1475
+ */
1476
+ private $json_objectStack = array();
1477
+
1478
+
1479
+ /**
1480
+ * convert a string from one UTF-8 char to one UTF-16 char
1481
+ *
1482
+ * Normally should be handled by mb_convert_encoding, but
1483
+ * provides a slower PHP-only method for installations
1484
+ * that lack the multibye string extension.
1485
+ *
1486
+ * @param string $utf8 UTF-8 character
1487
+ * @return string UTF-16 character
1488
+ * @access private
1489
+ */
1490
+ private function json_utf82utf16($utf8)
1491
+ {
1492
+ // oh please oh please oh please oh please oh please
1493
+ if (function_exists('mb_convert_encoding')) {
1494
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
1495
+ }
1496
+
1497
+ switch(strlen($utf8)) {
1498
+ case 1:
1499
+ // this case should never be reached, because we are in ASCII range
1500
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1501
+ return $utf8;
1502
+
1503
+ case 2:
1504
+ // return a UTF-16 character from a 2-byte UTF-8 char
1505
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1506
+ return chr(0x07 & (ord($utf8{0}) >> 2))
1507
+ . chr((0xC0 & (ord($utf8{0}) << 6))
1508
+ | (0x3F & ord($utf8{1})));
1509
+
1510
+ case 3:
1511
+ // return a UTF-16 character from a 3-byte UTF-8 char
1512
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1513
+ return chr((0xF0 & (ord($utf8{0}) << 4))
1514
+ | (0x0F & (ord($utf8{1}) >> 2)))
1515
+ . chr((0xC0 & (ord($utf8{1}) << 6))
1516
+ | (0x7F & ord($utf8{2})));
1517
+ }
1518
+
1519
+ // ignoring UTF-32 for now, sorry
1520
+ return '';
1521
+ }
1522
+
1523
+ /**
1524
+ * encodes an arbitrary variable into JSON format
1525
+ *
1526
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
1527
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
1528
+ * if var is a strng, note that encode() always expects it
1529
+ * to be in ASCII or UTF-8 format!
1530
+ *
1531
+ * @return mixed JSON string representation of input var or an error if a problem occurs
1532
+ * @access public
1533
+ */
1534
+ private function json_encode($var)
1535
+ {
1536
+
1537
+ if (is_object($var)) {
1538
+ if (in_array($var,$this->json_objectStack)) {
1539
+ return '"** Recursion **"';
1540
+ }
1541
+ }
1542
+
1543
+ switch (gettype($var)) {
1544
+ case 'boolean':
1545
+ return $var ? 'true' : 'false';
1546
+
1547
+ case 'NULL':
1548
+ return 'null';
1549
+
1550
+ case 'integer':
1551
+ return (int) $var;
1552
+
1553
+ case 'double':
1554
+ case 'float':
1555
+ return (float) $var;
1556
+
1557
+ case 'string':
1558
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
1559
+ $ascii = '';
1560
+ $strlen_var = strlen($var);
1561
+
1562
+ /*
1563
+ * Iterate over every character in the string,
1564
+ * escaping with a slash or encoding to UTF-8 where necessary
1565
+ */
1566
+ for ($c = 0; $c < $strlen_var; ++$c) {
1567
+
1568
+ $ord_var_c = ord($var{$c});
1569
+
1570
+ switch (true) {
1571
+ case $ord_var_c == 0x08:
1572
+ $ascii .= '\b';
1573
+ break;
1574
+ case $ord_var_c == 0x09:
1575
+ $ascii .= '\t';
1576
+ break;
1577
+ case $ord_var_c == 0x0A:
1578
+ $ascii .= '\n';
1579
+ break;
1580
+ case $ord_var_c == 0x0C:
1581
+ $ascii .= '\f';
1582
+ break;
1583
+ case $ord_var_c == 0x0D:
1584
+ $ascii .= '\r';
1585
+ break;
1586
+
1587
+ case $ord_var_c == 0x22:
1588
+ case $ord_var_c == 0x2F:
1589
+ case $ord_var_c == 0x5C:
1590
+ // double quote, slash, slosh
1591
+ $ascii .= '\\'.$var{$c};
1592
+ break;
1593
+
1594
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
1595
+ // characters U-00000000 - U-0000007F (same as ASCII)
1596
+ $ascii .= $var{$c};
1597
+ break;
1598
+
1599
+ case (($ord_var_c & 0xE0) == 0xC0):
1600
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
1601
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1602
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
1603
+ $c += 1;
1604
+ $utf16 = $this->json_utf82utf16($char);
1605
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1606
+ break;
1607
+
1608
+ case (($ord_var_c & 0xF0) == 0xE0):
1609
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1610
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1611
+ $char = pack('C*', $ord_var_c,
1612
+ ord($var{$c + 1}),
1613
+ ord($var{$c + 2}));
1614
+ $c += 2;
1615
+ $utf16 = $this->json_utf82utf16($char);
1616
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1617
+ break;
1618
+
1619
+ case (($ord_var_c & 0xF8) == 0xF0):
1620
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
1621
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1622
+ $char = pack('C*', $ord_var_c,
1623
+ ord($var{$c + 1}),
1624
+ ord($var{$c + 2}),
1625
+ ord($var{$c + 3}));
1626
+ $c += 3;
1627
+ $utf16 = $this->json_utf82utf16($char);
1628
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1629
+ break;
1630
+
1631
+ case (($ord_var_c & 0xFC) == 0xF8):
1632
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
1633
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1634
+ $char = pack('C*', $ord_var_c,
1635
+ ord($var{$c + 1}),
1636
+ ord($var{$c + 2}),
1637
+ ord($var{$c + 3}),
1638
+ ord($var{$c + 4}));
1639
+ $c += 4;
1640
+ $utf16 = $this->json_utf82utf16($char);
1641
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1642
+ break;
1643
+
1644
+ case (($ord_var_c & 0xFE) == 0xFC):
1645
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1646
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1647
+ $char = pack('C*', $ord_var_c,
1648
+ ord($var{$c + 1}),
1649
+ ord($var{$c + 2}),
1650
+ ord($var{$c + 3}),
1651
+ ord($var{$c + 4}),
1652
+ ord($var{$c + 5}));
1653
+ $c += 5;
1654
+ $utf16 = $this->json_utf82utf16($char);
1655
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1656
+ break;
1657
+ }
1658
+ }
1659
+
1660
+ return '"'.$ascii.'"';
1661
+
1662
+ case 'array':
1663
+ /*
1664
+ * As per JSON spec if any array key is not an integer
1665
+ * we must treat the the whole array as an object. We
1666
+ * also try to catch a sparsely populated associative
1667
+ * array with numeric keys here because some JS engines
1668
+ * will create an array with empty indexes up to
1669
+ * max_index which can cause memory issues and because
1670
+ * the keys, which may be relevant, will be remapped
1671
+ * otherwise.
1672
+ *
1673
+ * As per the ECMA and JSON specification an object may
1674
+ * have any string as a property. Unfortunately due to
1675
+ * a hole in the ECMA specification if the key is a
1676
+ * ECMA reserved word or starts with a digit the
1677
+ * parameter is only accessible using ECMAScript's
1678
+ * bracket notation.
1679
+ */
1680
+
1681
+ // treat as a JSON object
1682
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
1683
+
1684
+ $this->json_objectStack[] = $var;
1685
+
1686
+ $properties = array_map(array($this, 'json_name_value'),
1687
+ array_keys($var),
1688
+ array_values($var));
1689
+
1690
+ array_pop($this->json_objectStack);
1691
+
1692
+ foreach($properties as $property) {
1693
+ if ($property instanceof Exception) {
1694
+ return $property;
1695
+ }
1696
+ }
1697
+
1698
+ return '{' . join(',', $properties) . '}';
1699
+ }
1700
+
1701
+ $this->json_objectStack[] = $var;
1702
+
1703
+ // treat it like a regular array
1704
+ $elements = array_map(array($this, 'json_encode'), $var);
1705
+
1706
+ array_pop($this->json_objectStack);
1707
+
1708
+ foreach($elements as $element) {
1709
+ if ($element instanceof Exception) {
1710
+ return $element;
1711
+ }
1712
+ }
1713
+
1714
+ return '[' . join(',', $elements) . ']';
1715
+
1716
+ case 'object':
1717
+ $vars = self::encodeObject($var);
1718
+
1719
+ $this->json_objectStack[] = $var;
1720
+
1721
+ $properties = array_map(array($this, 'json_name_value'),
1722
+ array_keys($vars),
1723
+ array_values($vars));
1724
+
1725
+ array_pop($this->json_objectStack);
1726
+
1727
+ foreach($properties as $property) {
1728
+ if ($property instanceof Exception) {
1729
+ return $property;
1730
+ }
1731
+ }
1732
+
1733
+ return '{' . join(',', $properties) . '}';
1734
+
1735
+ default:
1736
+ return null;
1737
+ }
1738
+ }
1739
+
1740
+ /**
1741
+ * array-walking function for use in generating JSON-formatted name-value pairs
1742
+ *
1743
+ * @param string $name name of key to use
1744
+ * @param mixed $value reference to an array element to be encoded
1745
+ *
1746
+ * @return string JSON-formatted name-value pair, like '"name":value'
1747
+ * @access private
1748
+ */
1749
+ private function json_name_value($name, $value)
1750
+ {
1751
+ // Encoding the $GLOBALS PHP array causes an infinite loop
1752
+ // if the recursion is not reset here as it contains
1753
+ // a reference to itself. This is the only way I have come up
1754
+ // with to stop infinite recursion in this case.
1755
+ if ($name=='GLOBALS'
1756
+ && is_array($value)
1757
+ && array_key_exists('GLOBALS',$value)) {
1758
+ $value['GLOBALS'] = '** Recursion **';
1759
+ }
1760
+
1761
+ $encoded_value = $this->json_encode($value);
1762
+
1763
+ if ($encoded_value instanceof Exception) {
1764
+ return $encoded_value;
1765
+ }
1766
+
1767
+ return $this->json_encode(strval($name)) . ':' . $encoded_value;
1768
+ }
1769
+
1770
+ /**
1771
+ * @deprecated
1772
+ */
1773
+ public function setProcessorUrl($URL)
1774
+ {
1775
+ trigger_error("The FirePHP::setProcessorUrl() method is no longer supported", E_USER_DEPRECATED);
1776
+ }
1777
+
1778
+ /**
1779
+ * @deprecated
1780
+ */
1781
+ public function setRendererUrl($URL)
1782
+ {
1783
+ trigger_error("The FirePHP::setRendererUrl() method is no longer supported", E_USER_DEPRECATED);
1784
+ }
1785
+ }
classes/libs/FirePHPCore/FirePHP.class.php4 ADDED
@@ -0,0 +1,1363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\libs\FirePHPCore;
3
+ /**
4
+ * *** BEGIN LICENSE BLOCK *****
5
+ *
6
+ * This file is part of FirePHP (http://www.firephp.org/).
7
+ *
8
+ * Software License Agreement (New BSD License)
9
+ *
10
+ * Copyright (c) 2006-2010, Christoph Dorn
11
+ * All rights reserved.
12
+ *
13
+ * Redistribution and use in source and binary forms, with or without modification,
14
+ * are permitted provided that the following conditions are met:
15
+ *
16
+ * * Redistributions of source code must retain the above copyright notice,
17
+ * this list of conditions and the following disclaimer.
18
+ *
19
+ * * Redistributions in binary form must reproduce the above copyright notice,
20
+ * this list of conditions and the following disclaimer in the documentation
21
+ * and/or other materials provided with the distribution.
22
+ *
23
+ * * Neither the name of Christoph Dorn nor the names of its
24
+ * contributors may be used to endorse or promote products derived from this
25
+ * software without specific prior written permission.
26
+ *
27
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ *
38
+ * ***** END LICENSE BLOCK *****
39
+ *
40
+ * This verion of FirePHPCore is for use with PHP4. If you do not require PHP4
41
+ * compatibility, it is suggested you use FirePHPCore.class.php instead.
42
+ *
43
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
44
+ * @author Christoph Dorn <christoph@christophdorn.com>
45
+ * @author Michael Day <manveru.alma@gmail.com>
46
+ * @license http://www.opensource.org/licenses/bsd-license.php
47
+ * @package FirePHPCore
48
+ */
49
+
50
+ /**
51
+ * FirePHP version
52
+ *
53
+ * @var string
54
+ */
55
+ define('FirePHP_VERSION', '0.3'); // @pinf replace '0.3' with '%%package.version%%'
56
+
57
+ /**
58
+ * Firebug LOG level
59
+ *
60
+ * Logs a message to firebug console
61
+ *
62
+ * @var string
63
+ */
64
+ define('FirePHP_LOG', 'LOG');
65
+
66
+ /**
67
+ * Firebug INFO level
68
+ *
69
+ * Logs a message to firebug console and displays an info icon before the message
70
+ *
71
+ * @var string
72
+ */
73
+ define('FirePHP_INFO', 'INFO');
74
+
75
+ /**
76
+ * Firebug WARN level
77
+ *
78
+ * Logs a message to firebug console, displays a warning icon before the message and colors the line turquoise
79
+ *
80
+ * @var string
81
+ */
82
+ define('FirePHP_WARN', 'WARN');
83
+
84
+ /**
85
+ * Firebug ERROR level
86
+ *
87
+ * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count.
88
+ *
89
+ * @var string
90
+ */
91
+ define('FirePHP_ERROR', 'ERROR');
92
+
93
+ /**
94
+ * Dumps a variable to firebug's server panel
95
+ *
96
+ * @var string
97
+ */
98
+ define('FirePHP_DUMP', 'DUMP');
99
+
100
+ /**
101
+ * Displays a stack trace in firebug console
102
+ *
103
+ * @var string
104
+ */
105
+ define('FirePHP_TRACE', 'TRACE');
106
+
107
+ /**
108
+ * Displays a table in firebug console
109
+ *
110
+ * @var string
111
+ */
112
+ define('FirePHP_TABLE', 'TABLE');
113
+
114
+ /**
115
+ * Starts a group in firebug console
116
+ *
117
+ * @var string
118
+ */
119
+ define('FirePHP_GROUP_START', 'GROUP_START');
120
+
121
+ /**
122
+ * Ends a group in firebug console
123
+ *
124
+ * @var string
125
+ */
126
+ define('FirePHP_GROUP_END', 'GROUP_END');
127
+
128
+ /**
129
+ * Sends the given data to the FirePHP Firefox Extension.
130
+ * The data can be displayed in the Firebug Console or in the
131
+ * "Server" request tab.
132
+ *
133
+ * For more information see: http://www.firephp.org/
134
+ *
135
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
136
+ * @author Christoph Dorn <christoph@christophdorn.com>
137
+ * @author Michael Day <manveru.alma@gmail.com>
138
+ * @license http://www.opensource.org/licenses/bsd-license.php
139
+ * @package FirePHPCore
140
+ */
141
+ class FirePHP {
142
+ /**
143
+ * Wildfire protocol message index
144
+ *
145
+ * @var int
146
+ */
147
+ var $messageIndex = 1;
148
+
149
+ /**
150
+ * Options for the library
151
+ *
152
+ * @var array
153
+ */
154
+ var $options = array('maxObjectDepth' => 5,
155
+ 'maxArrayDepth' => 5,
156
+ 'useNativeJsonEncode' => true,
157
+ 'includeLineNumbers' => true);
158
+
159
+ /**
160
+ * Filters used to exclude object members when encoding
161
+ *
162
+ * @var array
163
+ */
164
+ var $objectFilters = array();
165
+
166
+ /**
167
+ * A stack of objects used to detect recursion during object encoding
168
+ *
169
+ * @var object
170
+ */
171
+ var $objectStack = array();
172
+
173
+ /**
174
+ * Flag to enable/disable logging
175
+ *
176
+ * @var boolean
177
+ */
178
+ var $enabled = true;
179
+
180
+ /**
181
+ * The object constructor
182
+ */
183
+ function FirePHP() {
184
+ }
185
+
186
+
187
+ /**
188
+ * When the object gets serialized only include specific object members.
189
+ *
190
+ * @return array
191
+ */
192
+ function __sleep() {
193
+ return array('options', 'objectFilters', 'enabled');
194
+ }
195
+
196
+ /**
197
+ * Gets singleton instance of FirePHP
198
+ *
199
+ * @param boolean $AutoCreate
200
+ *
201
+ * @return FirePHP
202
+ */
203
+ function &getInstance($AutoCreate = false) {
204
+ global $FirePHP_Instance;
205
+
206
+ if ($AutoCreate === true && !$FirePHP_Instance) {
207
+ $FirePHP_Instance = new FirePHP();
208
+ }
209
+
210
+ return $FirePHP_Instance;
211
+ }
212
+
213
+ /**
214
+ * Enable and disable logging to Firebug
215
+ *
216
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
217
+ *
218
+ * @return void
219
+ */
220
+ function setEnabled($Enabled) {
221
+ $this->enabled = $Enabled;
222
+ }
223
+
224
+ /**
225
+ * Check if logging is enabled
226
+ *
227
+ * @return boolean TRUE if enabled
228
+ */
229
+ function getEnabled() {
230
+ return $this->enabled;
231
+ }
232
+
233
+ /**
234
+ * Specify a filter to be used when encoding an object
235
+ *
236
+ * Filters are used to exclude object members.
237
+ *
238
+ * @param string $Class The class name of the object
239
+ * @param array $Filter An array of members to exclude
240
+ *
241
+ * @return void
242
+ */
243
+ function setObjectFilter($Class, $Filter) {
244
+ $this->objectFilters[strtolower($Class)] = $Filter;
245
+ }
246
+
247
+ /**
248
+ * Set some options for the library
249
+ *
250
+ * Options:
251
+ * - maxObjectDepth: The maximum depth to traverse objects (default: 5)
252
+ * - maxArrayDepth: The maximum depth to traverse arrays (default: 5)
253
+ * - useNativeJsonEncode: If true will use json_encode() (default: true)
254
+ * - includeLineNumbers: If true will include line numbers and filenames (default: true)
255
+ *
256
+ * @param array $Options The options to be set
257
+ *
258
+ * @return void
259
+ */
260
+ function setOptions($Options) {
261
+ $this->options = array_merge($this->options, $Options);
262
+ }
263
+
264
+ /**
265
+ * Get options from the library
266
+ *
267
+ * @return array The currently set options
268
+ */
269
+ function getOptions() {
270
+ return $this->options;
271
+ }
272
+
273
+ /**
274
+ * Register FirePHP as your error handler
275
+ *
276
+ * Will use FirePHP to log each php error.
277
+ *
278
+ * @return mixed Returns a string containing the previously defined error handler (if any)
279
+ */
280
+ function registerErrorHandler() {
281
+ //NOTE: The following errors will not be caught by this error handler:
282
+ // E_ERROR, E_PARSE, E_CORE_ERROR,
283
+ // E_CORE_WARNING, E_COMPILE_ERROR,
284
+ // E_COMPILE_WARNING, E_STRICT
285
+
286
+ return set_error_handler(array($this, 'errorHandler'));
287
+ }
288
+
289
+ /**
290
+ * FirePHP's error handler
291
+ *
292
+ * Logs each php error that will occur.
293
+ *
294
+ * @param int $errno
295
+ * @param string $errstr
296
+ * @param string $errfile
297
+ * @param int $errline
298
+ * @param array $errcontext
299
+ */
300
+ function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
301
+ global $FirePHP_Instance;
302
+ // Don't log error if error reporting is switched off
303
+ if (error_reporting() == 0) {
304
+ return;
305
+ }
306
+ // Only log error for errors we are asking for
307
+ if (error_reporting() & $errno) {
308
+ $FirePHP_Instance->group($errstr);
309
+ $FirePHP_Instance->error("{$errfile}, line $errline");
310
+ $FirePHP_Instance->groupEnd();
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Register FirePHP driver as your assert callback
316
+ *
317
+ * @return mixed Returns the original setting
318
+ */
319
+ function registerAssertionHandler() {
320
+ return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler'));
321
+ }
322
+
323
+ /**
324
+ * FirePHP's assertion handler
325
+ *
326
+ * Logs all assertions to your firebug console and then stops the script.
327
+ *
328
+ * @param string $file File source of assertion
329
+ * @param int $line Line source of assertion
330
+ * @param mixed $code Assertion code
331
+ */
332
+ function assertionHandler($file, $line, $code) {
333
+ $this->fb($code, 'Assertion Failed', FirePHP_ERROR, array('File' => $file, 'Line' => $line));
334
+ }
335
+
336
+ /**
337
+ * Set custom processor url for FirePHP
338
+ *
339
+ * @param string $URL
340
+ */
341
+ function setProcessorUrl($URL) {
342
+ $this->setHeader('X-FirePHP-ProcessorURL', $URL);
343
+ }
344
+
345
+ /**
346
+ * Set custom renderer url for FirePHP
347
+ *
348
+ * @param string $URL
349
+ */
350
+ function setRendererUrl($URL) {
351
+ $this->setHeader('X-FirePHP-RendererURL', $URL);
352
+ }
353
+
354
+ /**
355
+ * Start a group for following messages.
356
+ *
357
+ * Options:
358
+ * Collapsed: [true|false]
359
+ * Color: [#RRGGBB|ColorName]
360
+ *
361
+ * @param string $Name
362
+ * @param array $Options OPTIONAL Instructions on how to log the group
363
+ *
364
+ * @return true
365
+ * @throws Exception
366
+ */
367
+ function group($Name, $Options = null) {
368
+
369
+ if (!$Name) {
370
+ trigger_error('You must specify a label for the group!');
371
+ }
372
+
373
+ if ($Options) {
374
+ if (!is_array($Options)) {
375
+ trigger_error('Options must be defined as an array!');
376
+ }
377
+ if (array_key_exists('Collapsed', $Options)) {
378
+ $Options['Collapsed'] = ($Options['Collapsed']) ? 'true' : 'false';
379
+ }
380
+ }
381
+
382
+ return $this->fb(null, $Name, FirePHP_GROUP_START, $Options);
383
+ }
384
+
385
+ /**
386
+ * Ends a group you have started before
387
+ *
388
+ * @return true
389
+ * @throws Exception
390
+ */
391
+ function groupEnd() {
392
+ return $this->fb(null, null, FirePHP_GROUP_END);
393
+ }
394
+
395
+ /**
396
+ * Log object with label to firebug console
397
+ *
398
+ * @see FirePHP::LOG
399
+ *
400
+ * @param mixes $Object
401
+ * @param string $Label
402
+ *
403
+ * @return true
404
+ * @throws Exception
405
+ */
406
+ function log($Object, $Label = null) {
407
+ return $this->fb($Object, $Label, FirePHP_LOG);
408
+ }
409
+
410
+ /**
411
+ * Log object with label to firebug console
412
+ *
413
+ * @see FirePHP::INFO
414
+ *
415
+ * @param mixes $Object
416
+ * @param string $Label
417
+ *
418
+ * @return true
419
+ * @throws Exception
420
+ */
421
+ function info($Object, $Label = null) {
422
+ return $this->fb($Object, $Label, FirePHP_INFO);
423
+ }
424
+
425
+ /**
426
+ * Log object with label to firebug console
427
+ *
428
+ * @see FirePHP::WARN
429
+ *
430
+ * @param mixes $Object
431
+ * @param string $Label
432
+ *
433
+ * @return true
434
+ * @throws Exception
435
+ */
436
+ function warn($Object, $Label = null) {
437
+ return $this->fb($Object, $Label, FirePHP_WARN);
438
+ }
439
+
440
+ /**
441
+ * Log object with label to firebug console
442
+ *
443
+ * @see FirePHP::ERROR
444
+ *
445
+ * @param mixes $Object
446
+ * @param string $Label
447
+ *
448
+ * @return true
449
+ * @throws Exception
450
+ */
451
+ function error($Object, $Label = null) {
452
+ return $this->fb($Object, $Label, FirePHP_ERROR);
453
+ }
454
+
455
+ /**
456
+ * Dumps key and variable to firebug server panel
457
+ *
458
+ * @see FirePHP::DUMP
459
+ *
460
+ * @param string $Key
461
+ * @param mixed $Variable
462
+ *
463
+ * @return true
464
+ * @throws Exception
465
+ */
466
+ function dump($Key, $Variable) {
467
+ return $this->fb($Variable, $Key, FirePHP_DUMP);
468
+ }
469
+
470
+ /**
471
+ * Log a trace in the firebug console
472
+ *
473
+ * @see FirePHP::TRACE
474
+ *
475
+ * @param string $Label
476
+ *
477
+ * @return true
478
+ * @throws Exception
479
+ */
480
+ function trace($Label) {
481
+ return $this->fb($Label, FirePHP_TRACE);
482
+ }
483
+
484
+ /**
485
+ * Log a table in the firebug console
486
+ *
487
+ * @see FirePHP::TABLE
488
+ *
489
+ * @param string $Label
490
+ * @param string $Table
491
+ *
492
+ * @return true
493
+ * @throws Exception
494
+ */
495
+ function table($Label, $Table) {
496
+ return $this->fb($Table, $Label, FirePHP_TABLE);
497
+ }
498
+
499
+ /**
500
+ * Check if FirePHP is installed on client
501
+ *
502
+ * @return boolean
503
+ */
504
+ function detectClientExtension() {
505
+ // Check if FirePHP is installed on client via User-Agent header
506
+ if (@preg_match_all('/\sFirePHP\/([\.\d]*)\s?/si', $this->getUserAgent(), $m) &&
507
+ version_compare($m[1][0], '0.0.6', '>=')
508
+ ) {
509
+ return true;
510
+ } else
511
+ // Check if FirePHP is installed on client via X-FirePHP-Version header
512
+ if (@preg_match_all('/^([\.\d]*)$/si', $this->getRequestHeader("X-FirePHP-Version"), $m) &&
513
+ version_compare($m[1][0], '0.0.6', '>=')
514
+ ) {
515
+ return true;
516
+ }
517
+ return false;
518
+ }
519
+
520
+ /**
521
+ * Log varible to Firebug
522
+ *
523
+ * @see http://www.firephp.org/Wiki/Reference/Fb
524
+ *
525
+ * @param mixed $Object The variable to be logged
526
+ *
527
+ * @return true Return TRUE if message was added to headers, FALSE otherwise
528
+ * @throws Exception
529
+ */
530
+ function fb($Object) {
531
+
532
+ if (!$this->enabled) {
533
+ return false;
534
+ }
535
+
536
+ if (headers_sent($filename, $linenum)) {
537
+ trigger_error('Headers already sent in ' . $filename . ' on line ' . $linenum . '. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.');
538
+ }
539
+
540
+ $Type = null;
541
+ $Label = null;
542
+ $Options = array();
543
+
544
+ if (func_num_args() == 1) {
545
+ } else
546
+ if (func_num_args() == 2) {
547
+ switch (func_get_arg(1)) {
548
+ case FirePHP_LOG:
549
+ case FirePHP_INFO:
550
+ case FirePHP_WARN:
551
+ case FirePHP_ERROR:
552
+ case FirePHP_DUMP:
553
+ case FirePHP_TRACE:
554
+ case FirePHP_TABLE:
555
+ case FirePHP_GROUP_START:
556
+ case FirePHP_GROUP_END:
557
+ $Type = func_get_arg(1);
558
+ break;
559
+ default:
560
+ $Label = func_get_arg(1);
561
+ break;
562
+ }
563
+ } else
564
+ if (func_num_args() == 3) {
565
+ $Type = func_get_arg(2);
566
+ $Label = func_get_arg(1);
567
+ } else
568
+ if (func_num_args() == 4) {
569
+ $Type = func_get_arg(2);
570
+ $Label = func_get_arg(1);
571
+ $Options = func_get_arg(3);
572
+ } else {
573
+ trigger_error('Wrong number of arguments to fb() function!');
574
+ }
575
+
576
+
577
+ if (!$this->detectClientExtension()) {
578
+ return false;
579
+ }
580
+
581
+ $meta = array();
582
+ $skipFinalObjectEncode = false;
583
+
584
+ if ($Type == FirePHP_TRACE) {
585
+
586
+ $trace = debug_backtrace();
587
+ if (!$trace) return false;
588
+ for ($i = 0; $i < sizeof($trace); $i++) {
589
+
590
+ if (isset($trace[$i]['class'])
591
+ && isset($trace[$i]['file'])
592
+ && ($trace[$i]['class'] == 'FirePHP'
593
+ || $trace[$i]['class'] == 'FB')
594
+ && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
595
+ || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.class.php')
596
+ ) {
597
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
598
+ } else
599
+ if (isset($trace[$i]['class'])
600
+ && isset($trace[$i + 1]['file'])
601
+ && $trace[$i]['class'] == 'FirePHP'
602
+ && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php'
603
+ ) {
604
+ /* Skip fb() */
605
+ } else
606
+ if ($trace[$i]['function'] == 'fb'
607
+ || $trace[$i]['function'] == 'trace'
608
+ || $trace[$i]['function'] == 'send'
609
+ ) {
610
+ $Object = array('Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
611
+ 'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
612
+ 'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
613
+ 'Message' => $trace[$i]['args'][0],
614
+ 'File' => isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '',
615
+ 'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
616
+ 'Args' => isset($trace[$i]['args']) ? $this->encodeObject($trace[$i]['args']) : '',
617
+ 'Trace' => $this->_escapeTrace(array_splice($trace, $i + 1)));
618
+
619
+ $skipFinalObjectEncode = true;
620
+ $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
621
+ $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
622
+ break;
623
+ }
624
+ }
625
+
626
+ } else
627
+ if ($Type == FirePHP_TABLE) {
628
+
629
+ if (isset($Object[0]) && is_string($Object[0])) {
630
+ $Object[1] = $this->encodeTable($Object[1]);
631
+ } else {
632
+ $Object = $this->encodeTable($Object);
633
+ }
634
+
635
+ $skipFinalObjectEncode = true;
636
+
637
+ } else
638
+ if ($Type == FirePHP_GROUP_START) {
639
+
640
+ if (!$Label) {
641
+ trigger_error('You must specify a label for the group!');
642
+ }
643
+ } else {
644
+ if ($Type === null) {
645
+ $Type = FirePHP_LOG;
646
+ }
647
+ }
648
+
649
+ if ($this->options['includeLineNumbers']) {
650
+ if (!isset($meta['file']) || !isset($meta['line'])) {
651
+
652
+ $trace = debug_backtrace();
653
+ for ($i = 0; $trace && $i < sizeof($trace); $i++) {
654
+
655
+ if (isset($trace[$i]['class'])
656
+ && isset($trace[$i]['file'])
657
+ && ($trace[$i]['class'] == 'FirePHP'
658
+ || $trace[$i]['class'] == 'FB')
659
+ && (substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
660
+ || substr($this->_standardizePath($trace[$i]['file']), -29, 29) == 'FirePHPCore/FirePHP.class.php')
661
+ ) {
662
+ /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */
663
+ } else
664
+ if (isset($trace[$i]['class'])
665
+ && isset($trace[$i + 1]['file'])
666
+ && $trace[$i]['class'] == 'FirePHP'
667
+ && substr($this->_standardizePath($trace[$i + 1]['file']), -18, 18) == 'FirePHPCore/fb.php'
668
+ ) {
669
+ /* Skip fb() */
670
+ } else
671
+ if (isset($trace[$i]['file'])
672
+ && substr($this->_standardizePath($trace[$i]['file']), -18, 18) == 'FirePHPCore/fb.php'
673
+ ) {
674
+ /* Skip FB::fb() */
675
+ } else {
676
+ $meta['file'] = isset($trace[$i]['file']) ? $this->_escapeTraceFile($trace[$i]['file']) : '';
677
+ $meta['line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
678
+ break;
679
+ }
680
+ }
681
+
682
+ }
683
+ } else {
684
+ unset($meta['file']);
685
+ unset($meta['line']);
686
+ }
687
+
688
+ $this->setHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
689
+ $this->setHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . FirePHP_VERSION);
690
+
691
+ $structure_index = 1;
692
+ if ($Type == FirePHP_DUMP) {
693
+ $structure_index = 2;
694
+ $this->setHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
695
+ } else {
696
+ $this->setHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
697
+ }
698
+
699
+ if ($Type == FirePHP_DUMP) {
700
+ $msg = '{"' . $Label . '":' . $this->jsonEncode($Object, $skipFinalObjectEncode) . '}';
701
+ } else {
702
+ $msg_meta = $Options;
703
+ $msg_meta['Type'] = $Type;
704
+ if ($Label !== null) {
705
+ $msg_meta['Label'] = $Label;
706
+ }
707
+ if (isset($meta['file']) && !isset($msg_meta['File'])) {
708
+ $msg_meta['File'] = $meta['file'];
709
+ }
710
+ if (isset($meta['line']) && !isset($msg_meta['Line'])) {
711
+ $msg_meta['Line'] = $meta['line'];
712
+ }
713
+ $msg = '[' . $this->jsonEncode($msg_meta) . ',' . $this->jsonEncode($Object, $skipFinalObjectEncode) . ']';
714
+ }
715
+
716
+ $parts = explode("\n", chunk_split($msg, 5000, "\n"));
717
+
718
+ for ($i = 0; $i < count($parts); $i++) {
719
+
720
+ $part = $parts[$i];
721
+ if ($part) {
722
+
723
+ if (count($parts) > 2) {
724
+ // Message needs to be split into multiple parts
725
+ $this->setHeader('X-Wf-1-' . $structure_index . '-' . '1-' . $this->messageIndex,
726
+ (($i == 0) ? strlen($msg) : '')
727
+ . '|' . $part . '|'
728
+ . (($i < count($parts) - 2) ? '\\' : ''));
729
+ } else {
730
+ $this->setHeader('X-Wf-1-' . $structure_index . '-' . '1-' . $this->messageIndex,
731
+ strlen($part) . '|' . $part . '|');
732
+ }
733
+
734
+ $this->messageIndex++;
735
+
736
+ if ($this->messageIndex > 99999) {
737
+ trigger_error('Maximum number (99,999) of messages reached!');
738
+ }
739
+ }
740
+ }
741
+
742
+ $this->setHeader('X-Wf-1-Index', $this->messageIndex - 1);
743
+
744
+ return true;
745
+ }
746
+
747
+
748
+ /**
749
+ * Standardizes path for windows systems.
750
+ *
751
+ * @param string $Path
752
+ *
753
+ * @return string
754
+ */
755
+ function _standardizePath($Path) {
756
+ return preg_replace('/\\\\+/', '/', $Path);
757
+ }
758
+
759
+ /**
760
+ * Escape trace path for windows systems
761
+ *
762
+ * @param array $Trace
763
+ *
764
+ * @return array
765
+ */
766
+ function _escapeTrace($Trace) {
767
+ if (!$Trace) return $Trace;
768
+ for ($i = 0; $i < sizeof($Trace); $i++) {
769
+ if (isset($Trace[$i]['file'])) {
770
+ $Trace[$i]['file'] = $this->_escapeTraceFile($Trace[$i]['file']);
771
+ }
772
+ if (isset($Trace[$i]['args'])) {
773
+ $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']);
774
+ }
775
+ }
776
+ return $Trace;
777
+ }
778
+
779
+ /**
780
+ * Escape file information of trace for windows systems
781
+ *
782
+ * @param string $File
783
+ *
784
+ * @return string
785
+ */
786
+ function _escapeTraceFile($File) {
787
+ /* Check if we have a windows filepath */
788
+ if (strpos($File, '\\')) {
789
+ /* First strip down to single \ */
790
+
791
+ $file = preg_replace('/\\\\+/', '\\', $File);
792
+
793
+ return $file;
794
+ }
795
+ return $File;
796
+ }
797
+
798
+ /**
799
+ * Send header
800
+ *
801
+ * @param string $Name
802
+ * @param string_type $Value
803
+ */
804
+ function setHeader($Name, $Value) {
805
+ return header($Name . ': ' . $Value);
806
+ }
807
+
808
+ /**
809
+ * Get user agent
810
+ *
811
+ * @return string|false
812
+ */
813
+ function getUserAgent() {
814
+ if (!isset($_SERVER['HTTP_USER_AGENT'])) return false;
815
+ return $_SERVER['HTTP_USER_AGENT'];
816
+ }
817
+
818
+ /**
819
+ * Get all request headers
820
+ *
821
+ * @return array
822
+ */
823
+ function getAllRequestHeaders() {
824
+ $headers = array();
825
+ if (function_exists('getallheaders')) {
826
+ foreach (getallheaders() as $name => $value) {
827
+ $headers[strtolower($name)] = $value;
828
+ }
829
+ } else {
830
+ foreach ($_SERVER as $name => $value) {
831
+ if (substr($name, 0, 5) == 'HTTP_') {
832
+ $headers[strtolower(str_replace(' ', '-', str_replace('_', ' ', substr($name, 5))))] = $value;
833
+ }
834
+ }
835
+ }
836
+ return $headers;
837
+ }
838
+
839
+ /**
840
+ * Get a request header
841
+ *
842
+ * @return string|false
843
+ */
844
+ function getRequestHeader($Name) {
845
+ $headers = $this->getAllRequestHeaders();
846
+ if (isset($headers[strtolower($Name)])) {
847
+ return $headers[strtolower($Name)];
848
+ }
849
+ return false;
850
+ }
851
+
852
+ /**
853
+ * Encode an object into a JSON string
854
+ *
855
+ * Uses PHP's jeson_encode() if available
856
+ *
857
+ * @param object $Object The object to be encoded
858
+ *
859
+ * @return string The JSON string
860
+ */
861
+ function jsonEncode($Object, $skipObjectEncode = false) {
862
+ if (!$skipObjectEncode) {
863
+ $Object = $this->encodeObject($Object);
864
+ }
865
+
866
+ if (function_exists('json_encode')
867
+ && $this->options['useNativeJsonEncode'] != false
868
+ ) {
869
+
870
+ return json_encode($Object);
871
+ } else {
872
+ return $this->json_encode($Object);
873
+ }
874
+ }
875
+
876
+ /**
877
+ * Encodes a table by encoding each row and column with encodeObject()
878
+ *
879
+ * @param array $Table The table to be encoded
880
+ *
881
+ * @return array
882
+ */
883
+ function encodeTable($Table) {
884
+
885
+ if (!$Table) return $Table;
886
+
887
+ $new_table = array();
888
+ foreach ($Table as $row) {
889
+
890
+ if (is_array($row)) {
891
+ $new_row = array();
892
+
893
+ foreach ($row as $item) {
894
+ $new_row[] = $this->encodeObject($item);
895
+ }
896
+
897
+ $new_table[] = $new_row;
898
+ }
899
+ }
900
+
901
+ return $new_table;
902
+ }
903
+
904
+ /**
905
+ * Encodes an object
906
+ *
907
+ * @param Object $Object The object to be encoded
908
+ * @param int $Depth The current traversal depth
909
+ *
910
+ * @return array All members of the object
911
+ */
912
+ function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) {
913
+ $return = array();
914
+
915
+ if (is_resource($Object)) {
916
+
917
+ return '** ' . (string)$Object . ' **';
918
+
919
+ } else
920
+ if (is_object($Object)) {
921
+
922
+ if ($ObjectDepth > $this->options['maxObjectDepth']) {
923
+ return '** Max Object Depth (' . $this->options['maxObjectDepth'] . ') **';
924
+ }
925
+
926
+ foreach ($this->objectStack as $refVal) {
927
+ if ($refVal === $Object) {
928
+ return '** Recursion (' . get_class($Object) . ') **';
929
+ }
930
+ }
931
+ array_push($this->objectStack, $Object);
932
+
933
+ $return['__className'] = $class = get_class($Object);
934
+ $class_lower = strtolower($class);
935
+
936
+ $members = (array)$Object;
937
+
938
+ // Include all members that are not defined in the class
939
+ // but exist in the object
940
+ foreach ($members as $raw_name => $value) {
941
+
942
+ $name = $raw_name;
943
+
944
+ if ($name{0} == "\0") {
945
+ $parts = explode("\0", $name);
946
+ $name = $parts[2];
947
+ }
948
+
949
+ if (!isset($properties[$name])) {
950
+ $name = 'undeclared:' . $name;
951
+
952
+ if (!(isset($this->objectFilters[$class_lower])
953
+ && is_array($this->objectFilters[$class_lower])
954
+ && in_array($raw_name, $this->objectFilters[$class_lower]))
955
+ ) {
956
+
957
+ $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1);
958
+ } else {
959
+ $return[$name] = '** Excluded by Filter **';
960
+ }
961
+ }
962
+ }
963
+
964
+ array_pop($this->objectStack);
965
+
966
+ } elseif (is_array($Object)) {
967
+
968
+ if ($ArrayDepth > $this->options['maxArrayDepth']) {
969
+ return '** Max Array Depth (' . $this->options['maxArrayDepth'] . ') **';
970
+ }
971
+
972
+ foreach ($Object as $key => $val) {
973
+
974
+ // Encoding the $GLOBALS PHP array causes an infinite loop
975
+ // if the recursion is not reset here as it contains
976
+ // a reference to itself. This is the only way I have come up
977
+ // with to stop infinite recursion in this case.
978
+ if ($key == 'GLOBALS'
979
+ && is_array($val)
980
+ && array_key_exists('GLOBALS', $val)
981
+ ) {
982
+ $val['GLOBALS'] = '** Recursion (GLOBALS) **';
983
+ }
984
+
985
+ $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1);
986
+ }
987
+ } else {
988
+ if ($this->is_utf8($Object)) {
989
+ return $Object;
990
+ } else {
991
+ return utf8_encode($Object);
992
+ }
993
+ }
994
+ return $return;
995
+
996
+ }
997
+
998
+ /**
999
+ * Returns true if $string is valid UTF-8 and false otherwise.
1000
+ *
1001
+ * @param mixed $str String to be tested
1002
+ *
1003
+ * @return boolean
1004
+ */
1005
+ function is_utf8($str) {
1006
+ $c = 0;
1007
+ $b = 0;
1008
+ $bits = 0;
1009
+ $len = strlen($str);
1010
+ for ($i = 0; $i < $len; $i++) {
1011
+ $c = ord($str[$i]);
1012
+ if ($c > 128) {
1013
+ if (($c >= 254)) return false;
1014
+ elseif ($c >= 252) $bits = 6;
1015
+ elseif ($c >= 248) $bits = 5;
1016
+ elseif ($c >= 240) $bits = 4;
1017
+ elseif ($c >= 224) $bits = 3;
1018
+ elseif ($c >= 192) $bits = 2;
1019
+ else return false;
1020
+ if (($i + $bits) > $len) return false;
1021
+ while ($bits > 1) {
1022
+ $i++;
1023
+ $b = ord($str[$i]);
1024
+ if ($b < 128 || $b > 191) return false;
1025
+ $bits--;
1026
+ }
1027
+ }
1028
+ }
1029
+ return true;
1030
+ }
1031
+
1032
+ /**
1033
+ * Converts to and from JSON format.
1034
+ *
1035
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
1036
+ * format. It is easy for humans to read and write. It is easy for machines
1037
+ * to parse and generate. It is based on a subset of the JavaScript
1038
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
1039
+ * This feature can also be found in Python. JSON is a text format that is
1040
+ * completely language independent but uses conventions that are familiar
1041
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
1042
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
1043
+ * ideal data-interchange language.
1044
+ *
1045
+ * This package provides a simple encoder and decoder for JSON notation. It
1046
+ * is intended for use with client-side Javascript applications that make
1047
+ * use of HTTPRequest to perform server communication functions - data can
1048
+ * be encoded into JSON notation for use in a client-side javascript, or
1049
+ * decoded from incoming Javascript requests. JSON format is native to
1050
+ * Javascript, and can be directly eval()'ed with no further parsing
1051
+ * overhead
1052
+ *
1053
+ * All strings should be in ASCII or UTF-8 format!
1054
+ *
1055
+ * LICENSE: Redistribution and use in source and binary forms, with or
1056
+ * without modification, are permitted provided that the following
1057
+ * conditions are met: Redistributions of source code must retain the
1058
+ * above copyright notice, this list of conditions and the following
1059
+ * disclaimer. Redistributions in binary form must reproduce the above
1060
+ * copyright notice, this list of conditions and the following disclaimer
1061
+ * in the documentation and/or other materials provided with the
1062
+ * distribution.
1063
+ *
1064
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
1065
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1066
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
1067
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1068
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1069
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1070
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1071
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1072
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1073
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1074
+ * DAMAGE.
1075
+ *
1076
+ * @category
1077
+ * @package Services_JSON
1078
+ * @author Michal Migurski <mike-json@teczno.com>
1079
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
1080
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
1081
+ * @author Christoph Dorn <christoph@christophdorn.com>
1082
+ * @copyright 2005 Michal Migurski
1083
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
1084
+ * @license http://www.opensource.org/licenses/bsd-license.php
1085
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
1086
+ */
1087
+
1088
+
1089
+ /**
1090
+ * Keep a list of objects as we descend into the array so we can detect recursion.
1091
+ */
1092
+ var $json_objectStack = array();
1093
+
1094
+
1095
+ /**
1096
+ * convert a string from one UTF-8 char to one UTF-16 char
1097
+ *
1098
+ * Normally should be handled by mb_convert_encoding, but
1099
+ * provides a slower PHP-only method for installations
1100
+ * that lack the multibye string extension.
1101
+ *
1102
+ * @param string $utf8 UTF-8 character
1103
+ *
1104
+ * @return string UTF-16 character
1105
+ * @access private
1106
+ */
1107
+ function json_utf82utf16($utf8) {
1108
+ // oh please oh please oh please oh please oh please
1109
+ if (function_exists('mb_convert_encoding')) {
1110
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
1111
+ }
1112
+
1113
+ switch (strlen($utf8)) {
1114
+ case 1:
1115
+ // this case should never be reached, because we are in ASCII range
1116
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1117
+ return $utf8;
1118
+
1119
+ case 2:
1120
+ // return a UTF-16 character from a 2-byte UTF-8 char
1121
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1122
+ return chr(0x07 & (ord($utf8{0}) >> 2))
1123
+ . chr((0xC0 & (ord($utf8{0}) << 6))
1124
+ | (0x3F & ord($utf8{1})));
1125
+
1126
+ case 3:
1127
+ // return a UTF-16 character from a 3-byte UTF-8 char
1128
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1129
+ return chr((0xF0 & (ord($utf8{0}) << 4))
1130
+ | (0x0F & (ord($utf8{1}) >> 2)))
1131
+ . chr((0xC0 & (ord($utf8{1}) << 6))
1132
+ | (0x7F & ord($utf8{2})));
1133
+ }
1134
+
1135
+ // ignoring UTF-32 for now, sorry
1136
+ return '';
1137
+ }
1138
+
1139
+ /**
1140
+ * encodes an arbitrary variable into JSON format
1141
+ *
1142
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
1143
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
1144
+ * if var is a strng, note that encode() always expects it
1145
+ * to be in ASCII or UTF-8 format!
1146
+ *
1147
+ * @return mixed JSON string representation of input var or an error if a problem occurs
1148
+ * @access public
1149
+ */
1150
+ function json_encode($var) {
1151
+
1152
+ if (is_object($var)) {
1153
+ if (in_array($var, $this->json_objectStack)) {
1154
+ return '"** Recursion **"';
1155
+ }
1156
+ }
1157
+
1158
+ switch (gettype($var)) {
1159
+ case 'boolean':
1160
+ return $var ? 'true' : 'false';
1161
+
1162
+ case 'NULL':
1163
+ return 'null';
1164
+
1165
+ case 'integer':
1166
+ return (int)$var;
1167
+
1168
+ case 'double':
1169
+ case 'float':
1170
+ return (float)$var;
1171
+
1172
+ case 'string':
1173
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
1174
+ $ascii = '';
1175
+ $strlen_var = strlen($var);
1176
+
1177
+ /*
1178
+ * Iterate over every character in the string,
1179
+ * escaping with a slash or encoding to UTF-8 where necessary
1180
+ */
1181
+ for ($c = 0; $c < $strlen_var; ++$c) {
1182
+
1183
+ $ord_var_c = ord($var{$c});
1184
+
1185
+ switch (true) {
1186
+ case $ord_var_c == 0x08:
1187
+ $ascii .= '\b';
1188
+ break;
1189
+ case $ord_var_c == 0x09:
1190
+ $ascii .= '\t';
1191
+ break;
1192
+ case $ord_var_c == 0x0A:
1193
+ $ascii .= '\n';
1194
+ break;
1195
+ case $ord_var_c == 0x0C:
1196
+ $ascii .= '\f';
1197
+ break;
1198
+ case $ord_var_c == 0x0D:
1199
+ $ascii .= '\r';
1200
+ break;
1201
+
1202
+ case $ord_var_c == 0x22:
1203
+ case $ord_var_c == 0x2F:
1204
+ case $ord_var_c == 0x5C:
1205
+ // double quote, slash, slosh
1206
+ $ascii .= '\\' . $var{$c};
1207
+ break;
1208
+
1209
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
1210
+ // characters U-00000000 - U-0000007F (same as ASCII)
1211
+ $ascii .= $var{$c};
1212
+ break;
1213
+
1214
+ case (($ord_var_c & 0xE0) == 0xC0):
1215
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
1216
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1217
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
1218
+ $c += 1;
1219
+ $utf16 = $this->json_utf82utf16($char);
1220
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1221
+ break;
1222
+
1223
+ case (($ord_var_c & 0xF0) == 0xE0):
1224
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1225
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1226
+ $char = pack('C*', $ord_var_c,
1227
+ ord($var{$c + 1}),
1228
+ ord($var{$c + 2}));
1229
+ $c += 2;
1230
+ $utf16 = $this->json_utf82utf16($char);
1231
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1232
+ break;
1233
+
1234
+ case (($ord_var_c & 0xF8) == 0xF0):
1235
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
1236
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1237
+ $char = pack('C*', $ord_var_c,
1238
+ ord($var{$c + 1}),
1239
+ ord($var{$c + 2}),
1240
+ ord($var{$c + 3}));
1241
+ $c += 3;
1242
+ $utf16 = $this->json_utf82utf16($char);
1243
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1244
+ break;
1245
+
1246
+ case (($ord_var_c & 0xFC) == 0xF8):
1247
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
1248
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1249
+ $char = pack('C*', $ord_var_c,
1250
+ ord($var{$c + 1}),
1251
+ ord($var{$c + 2}),
1252
+ ord($var{$c + 3}),
1253
+ ord($var{$c + 4}));
1254
+ $c += 4;
1255
+ $utf16 = $this->json_utf82utf16($char);
1256
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1257
+ break;
1258
+
1259
+ case (($ord_var_c & 0xFE) == 0xFC):
1260
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1261
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1262
+ $char = pack('C*', $ord_var_c,
1263
+ ord($var{$c + 1}),
1264
+ ord($var{$c + 2}),
1265
+ ord($var{$c + 3}),
1266
+ ord($var{$c + 4}),
1267
+ ord($var{$c + 5}));
1268
+ $c += 5;
1269
+ $utf16 = $this->json_utf82utf16($char);
1270
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
1271
+ break;
1272
+ }
1273
+ }
1274
+
1275
+ return '"' . $ascii . '"';
1276
+
1277
+ case 'array':
1278
+ /*
1279
+ * As per JSON spec if any array key is not an integer
1280
+ * we must treat the the whole array as an object. We
1281
+ * also try to catch a sparsely populated associative
1282
+ * array with numeric keys here because some JS engines
1283
+ * will create an array with empty indexes up to
1284
+ * max_index which can cause memory issues and because
1285
+ * the keys, which may be relevant, will be remapped
1286
+ * otherwise.
1287
+ *
1288
+ * As per the ECMA and JSON specification an object may
1289
+ * have any string as a property. Unfortunately due to
1290
+ * a hole in the ECMA specification if the key is a
1291
+ * ECMA reserved word or starts with a digit the
1292
+ * parameter is only accessible using ECMAScript's
1293
+ * bracket notation.
1294
+ */
1295
+
1296
+ // treat as a JSON object
1297
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
1298
+
1299
+ $this->json_objectStack[] = $var;
1300
+
1301
+ $properties = array_map(array($this, 'json_name_value'),
1302
+ array_keys($var),
1303
+ array_values($var));
1304
+
1305
+ array_pop($this->json_objectStack);
1306
+
1307
+ return '{' . join(',', $properties) . '}';
1308
+ }
1309
+
1310
+ $this->json_objectStack[] = $var;
1311
+
1312
+ // treat it like a regular array
1313
+ $elements = array_map(array($this, 'json_encode'), $var);
1314
+
1315
+ array_pop($this->json_objectStack);
1316
+
1317
+ return '[' . join(',', $elements) . ']';
1318
+
1319
+ case 'object':
1320
+ $vars = FirePHP::encodeObject($var);
1321
+
1322
+ $this->json_objectStack[] = $var;
1323
+
1324
+ $properties = array_map(array($this, 'json_name_value'),
1325
+ array_keys($vars),
1326
+ array_values($vars));
1327
+
1328
+ array_pop($this->json_objectStack);
1329
+
1330
+ return '{' . join(',', $properties) . '}';
1331
+
1332
+ default:
1333
+ return null;
1334
+ }
1335
+ }
1336
+
1337
+ /**
1338
+ * array-walking function for use in generating JSON-formatted name-value pairs
1339
+ *
1340
+ * @param string $name name of key to use
1341
+ * @param mixed $value reference to an array element to be encoded
1342
+ *
1343
+ * @return string JSON-formatted name-value pair, like '"name":value'
1344
+ * @access private
1345
+ */
1346
+ function json_name_value($name, $value) {
1347
+ // Encoding the $GLOBALS PHP array causes an infinite loop
1348
+ // if the recursion is not reset here as it contains
1349
+ // a reference to itself. This is the only way I have come up
1350
+ // with to stop infinite recursion in this case.
1351
+ if ($name == 'GLOBALS'
1352
+ && is_array($value)
1353
+ && array_key_exists('GLOBALS', $value)
1354
+ ) {
1355
+ $value['GLOBALS'] = '** Recursion **';
1356
+ }
1357
+
1358
+ $encoded_value = $this->json_encode($value);
1359
+
1360
+ return $this->json_encode(strval($name)) . ':' . $encoded_value;
1361
+ }
1362
+ }
1363
+
classes/libs/FirePHPCore/fb.php ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\libs\FirePHPCore;
3
+
4
+ use mp_timetable\classes\libs\FirePHPCore\FirePHP;
5
+
6
+ /* ***** BEGIN LICENSE BLOCK *****
7
+ *
8
+ * This file is part of FirePHP (http://www.firephp.org/).
9
+ *
10
+ * Software License Agreement (New BSD License)
11
+ *
12
+ * Copyright (c) 2006-2010, Christoph Dorn
13
+ * All rights reserved.
14
+ *
15
+ * Redistribution and use in source and binary forms, with or without modification,
16
+ * are permitted provided that the following conditions are met:
17
+ *
18
+ * * Redistributions of source code must retain the above copyright notice,
19
+ * this list of conditions and the following disclaimer.
20
+ *
21
+ * * Redistributions in binary form must reproduce the above copyright notice,
22
+ * this list of conditions and the following disclaimer in the documentation
23
+ * and/or other materials provided with the distribution.
24
+ *
25
+ * * Neither the name of Christoph Dorn nor the names of its
26
+ * contributors may be used to endorse or promote products derived from this
27
+ * software without specific prior written permission.
28
+ *
29
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
30
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
32
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
33
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
35
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
36
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
+ *
40
+ * ***** END LICENSE BLOCK *****
41
+ *
42
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
43
+ * @author Christoph Dorn <christoph@christophdorn.com>
44
+ * @license http://www.opensource.org/licenses/bsd-license.php
45
+ * @package FirePHPCore
46
+ */
47
+
48
+ if (!class_exists('FirePHP')) {
49
+ require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'FirePHP.class.php';
50
+ }
51
+
52
+ /**
53
+ * Sends the given data to the FirePHP Firefox Extension.
54
+ * The data can be displayed in the Firebug Console or in the
55
+ * "Server" request tab.
56
+ *
57
+ * @see http://www.firephp.org/Wiki/Reference/Fb
58
+ *
59
+ * @param mixed $Object
60
+ *
61
+ * @return true
62
+ * @throws Exception
63
+ */
64
+ function fb() {
65
+ $instance = FirePHP::getInstance(true);
66
+
67
+ $args = func_get_args();
68
+ return call_user_func_array(array($instance, 'fb'), $args);
69
+ }
70
+
71
+
72
+ class FB {
73
+ /**
74
+ * Enable and disable logging to Firebug
75
+ *
76
+ * @see FirePHP->setEnabled()
77
+ *
78
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
79
+ *
80
+ * @return void
81
+ */
82
+ public static function setEnabled($Enabled) {
83
+ $instance = FirePHP::getInstance(true);
84
+ $instance->setEnabled($Enabled);
85
+ }
86
+
87
+ /**
88
+ * Check if logging is enabled
89
+ *
90
+ * @see FirePHP->getEnabled()
91
+ * @return boolean TRUE if enabled
92
+ */
93
+ public static function getEnabled() {
94
+ $instance = FirePHP::getInstance(true);
95
+ return $instance->getEnabled();
96
+ }
97
+
98
+ /**
99
+ * Specify a filter to be used when encoding an object
100
+ *
101
+ * Filters are used to exclude object members.
102
+ *
103
+ * @see FirePHP->setObjectFilter()
104
+ *
105
+ * @param string $Class The class name of the object
106
+ * @param array $Filter An array or members to exclude
107
+ *
108
+ * @return void
109
+ */
110
+ public static function setObjectFilter($Class, $Filter) {
111
+ $instance = FirePHP::getInstance(true);
112
+ $instance->setObjectFilter($Class, $Filter);
113
+ }
114
+
115
+ /**
116
+ * Set some options for the library
117
+ *
118
+ * @see FirePHP->setOptions()
119
+ *
120
+ * @param array $Options The options to be set
121
+ *
122
+ * @return void
123
+ */
124
+ public static function setOptions($Options) {
125
+ $instance = FirePHP::getInstance(true);
126
+ $instance->setOptions($Options);
127
+ }
128
+
129
+ /**
130
+ * Get options for the library
131
+ *
132
+ * @see FirePHP->getOptions()
133
+ * @return array The options
134
+ */
135
+ public static function getOptions() {
136
+ $instance = FirePHP::getInstance(true);
137
+ return $instance->getOptions();
138
+ }
139
+
140
+ /**
141
+ * Log object to firebug
142
+ *
143
+ * @see http://www.firephp.org/Wiki/Reference/Fb
144
+ *
145
+ * @param mixed $Object
146
+ *
147
+ * @return true
148
+ * @throws Exception
149
+ */
150
+ public static function send() {
151
+ $instance = FirePHP::getInstance(true);
152
+ $args = func_get_args();
153
+ return call_user_func_array(array($instance, 'fb'), $args);
154
+ }
155
+
156
+ /**
157
+ * Start a group for following messages
158
+ *
159
+ * Options:
160
+ * Collapsed: [true|false]
161
+ * Color: [#RRGGBB|ColorName]
162
+ *
163
+ * @param string $Name
164
+ * @param array $Options OPTIONAL Instructions on how to log the group
165
+ *
166
+ * @return true
167
+ */
168
+ public static function group($Name, $Options = null) {
169
+ $instance = FirePHP::getInstance(true);
170
+ return $instance->group($Name, $Options);
171
+ }
172
+
173
+ /**
174
+ * Ends a group you have started before
175
+ *
176
+ * @return true
177
+ * @throws Exception
178
+ */
179
+ public static function groupEnd() {
180
+ return self::send(null, null, FirePHP::GROUP_END);
181
+ }
182
+
183
+ /**
184
+ * Log object with label to firebug console
185
+ *
186
+ * @see FirePHP::LOG
187
+ *
188
+ * @param mixes $Object
189
+ * @param string $Label
190
+ *
191
+ * @return true
192
+ * @throws Exception
193
+ */
194
+ public static function log($Object, $Label = null) {
195
+ return self::send($Object, $Label, FirePHP::LOG);
196
+ }
197
+
198
+ /**
199
+ * Log object with label to firebug console
200
+ *
201
+ * @see FirePHP::INFO
202
+ *
203
+ * @param mixes $Object
204
+ * @param string $Label
205
+ *
206
+ * @return true
207
+ * @throws Exception
208
+ */
209
+ public static function info($Object, $Label = null) {
210
+ return self::send($Object, $Label, FirePHP::INFO);
211
+ }
212
+
213
+ /**
214
+ * Log object with label to firebug console
215
+ *
216
+ * @see FirePHP::WARN
217
+ *
218
+ * @param mixes $Object
219
+ * @param string $Label
220
+ *
221
+ * @return true
222
+ * @throws Exception
223
+ */
224
+ public static function warn($Object, $Label = null) {
225
+ return self::send($Object, $Label, FirePHP::WARN);
226
+ }
227
+
228
+ /**
229
+ * Log object with label to firebug console
230
+ *
231
+ * @see FirePHP::ERROR
232
+ *
233
+ * @param mixes $Object
234
+ * @param string $Label
235
+ *
236
+ * @return true
237
+ * @throws Exception
238
+ */
239
+ public static function error($Object, $Label = null) {
240
+ return self::send($Object, $Label, FirePHP::ERROR);
241
+ }
242
+
243
+ /**
244
+ * Dumps key and variable to firebug server panel
245
+ *
246
+ * @see FirePHP::DUMP
247
+ *
248
+ * @param string $Key
249
+ * @param mixed $Variable
250
+ *
251
+ * @return true
252
+ * @throws Exception
253
+ */
254
+ public static function dump($Key, $Variable) {
255
+ return self::send($Variable, $Key, FirePHP::DUMP);
256
+ }
257
+
258
+ /**
259
+ * Log a trace in the firebug console
260
+ *
261
+ * @see FirePHP::TRACE
262
+ *
263
+ * @param string $Label
264
+ *
265
+ * @return true
266
+ * @throws Exception
267
+ */
268
+ public static function trace($Label) {
269
+ return self::send($Label, FirePHP::TRACE);
270
+ }
271
+
272
+ /**
273
+ * Log a table in the firebug console
274
+ *
275
+ * @see FirePHP::TABLE
276
+ *
277
+ * @param string $Label
278
+ * @param string $Table
279
+ *
280
+ * @return true
281
+ * @throws Exception
282
+ */
283
+ public static function table($Label, $Table) {
284
+ return self::send($Table, $Label, FirePHP::TABLE);
285
+ }
286
+
287
+ }
classes/libs/FirePHPCore/fb.php4 ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\libs\FirePHPCore;
3
+ /* ***** BEGIN LICENSE BLOCK *****
4
+ *
5
+ * This file is part of FirePHP (http://www.firephp.org/).
6
+ *
7
+ * Software License Agreement (New BSD License)
8
+ *
9
+ * Copyright (c) 2006-2010, Christoph Dorn
10
+ * All rights reserved.
11
+ *
12
+ * Redistribution and use in source and binary forms, with or without modification,
13
+ * are permitted provided that the following conditions are met:
14
+ *
15
+ * * Redistributions of source code must retain the above copyright notice,
16
+ * this list of conditions and the following disclaimer.
17
+ *
18
+ * * Redistributions in binary form must reproduce the above copyright notice,
19
+ * this list of conditions and the following disclaimer in the documentation
20
+ * and/or other materials provided with the distribution.
21
+ *
22
+ * * Neither the name of Christoph Dorn nor the names of its
23
+ * contributors may be used to endorse or promote products derived from this
24
+ * software without specific prior written permission.
25
+ *
26
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
30
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
33
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ *
37
+ * ***** END LICENSE BLOCK *****
38
+ *
39
+ * @copyright Copyright (C) 2007-2009 Christoph Dorn
40
+ * @author Christoph Dorn <christoph@christophdorn.com>
41
+ * @author Michael Day <manveru.alma@gmail.com>
42
+ * @license http://www.opensource.org/licenses/bsd-license.php
43
+ * @package FirePHPCore
44
+ */
45
+
46
+ require_once dirname(__FILE__).'/FirePHP.class.php4';
47
+
48
+ /**
49
+ * Sends the given data to the FirePHP Firefox Extension.
50
+ * The data can be displayed in the Firebug Console or in the
51
+ * "Server" request tab.
52
+ *
53
+ * @see http://www.firephp.org/Wiki/Reference/Fb
54
+ * @param mixed $Object
55
+ * @return true
56
+ * @throws Exception
57
+ */
58
+ function fb()
59
+ {
60
+ $instance =& FirePHP::getInstance(true);
61
+
62
+ $args = func_get_args();
63
+ return call_user_func_array(array(&$instance,'fb'),$args);
64
+ }
65
+
66
+
67
+ class FB
68
+ {
69
+ /**
70
+ * Enable and disable logging to Firebug
71
+ *
72
+ * @see FirePHP->setEnabled()
73
+ * @param boolean $Enabled TRUE to enable, FALSE to disable
74
+ * @return void
75
+ */
76
+ function setEnabled($Enabled) {
77
+ $instance =& FirePHP::getInstance(true);
78
+ $instance->setEnabled($Enabled);
79
+ }
80
+
81
+ /**
82
+ * Check if logging is enabled
83
+ *
84
+ * @see FirePHP->getEnabled()
85
+ * @return boolean TRUE if enabled
86
+ */
87
+ function getEnabled() {
88
+ $instance =& FirePHP::getInstance(true);
89
+ return $instance->getEnabled();
90
+ }
91
+
92
+ /**
93
+ * Specify a filter to be used when encoding an object
94
+ *
95
+ * Filters are used to exclude object members.
96
+ *
97
+ * @see FirePHP->setObjectFilter()
98
+ * @param string $Class The class name of the object
99
+ * @param array $Filter An array or members to exclude
100
+ * @return void
101
+ */
102
+ function setObjectFilter($Class, $Filter) {
103
+ $instance =& FirePHP::getInstance(true);
104
+ $instance->setObjectFilter($Class, $Filter);
105
+ }
106
+
107
+ /**
108
+ * Set some options for the library
109
+ *
110
+ * @see FirePHP->setOptions()
111
+ * @param array $Options The options to be set
112
+ * @return void
113
+ */
114
+ function setOptions($Options) {
115
+ $instance =& FirePHP::getInstance(true);
116
+ $instance->setOptions($Options);
117
+ }
118
+
119
+ /**
120
+ * Get options for the library
121
+ *
122
+ * @see FirePHP->getOptions()
123
+ * @return array The options
124
+ */
125
+ function getOptions() {
126
+ $instance =& FirePHP::getInstance(true);
127
+ return $instance->getOptions();
128
+ }
129
+
130
+ /**
131
+ * Log object to firebug
132
+ *
133
+ * @see http://www.firephp.org/Wiki/Reference/Fb
134
+ * @param mixed $Object
135
+ * @return true
136
+ */
137
+ function send()
138
+ {
139
+ $instance =& FirePHP::getInstance(true);
140
+ $args = func_get_args();
141
+ return call_user_func_array(array(&$instance,'fb'),$args);
142
+ }
143
+
144
+ /**
145
+ * Start a group for following messages
146
+ *
147
+ * Options:
148
+ * Collapsed: [true|false]
149
+ * Color: [#RRGGBB|ColorName]
150
+ *
151
+ * @param string $Name
152
+ * @param array $Options OPTIONAL Instructions on how to log the group
153
+ * @return true
154
+ */
155
+ function group($Name, $Options=null) {
156
+ $instance =& FirePHP::getInstance(true);
157
+ return $instance->group($Name, $Options);
158
+ }
159
+
160
+ /**
161
+ * Ends a group you have started before
162
+ *
163
+ * @return true
164
+ */
165
+ function groupEnd() {
166
+ return FB::send(null, null, FirePHP_GROUP_END);
167
+ }
168
+
169
+ /**
170
+ * Log object with label to firebug console
171
+ *
172
+ * @see FirePHP::LOG
173
+ * @param mixes $Object
174
+ * @param string $Label
175
+ * @return true
176
+ */
177
+ function log($Object, $Label=null) {
178
+ return FB::send($Object, $Label, FirePHP_LOG);
179
+ }
180
+
181
+ /**
182
+ * Log object with label to firebug console
183
+ *
184
+ * @see FirePHP::INFO
185
+ * @param mixes $Object
186
+ * @param string $Label
187
+ * @return true
188
+ */
189
+ function info($Object, $Label=null) {
190
+ return FB::send($Object, $Label, FirePHP_INFO);
191
+ }
192
+
193
+ /**
194
+ * Log object with label to firebug console
195
+ *
196
+ * @see FirePHP::WARN
197
+ * @param mixes $Object
198
+ * @param string $Label
199
+ * @return true
200
+ */
201
+ function warn($Object, $Label=null) {
202
+ return FB::send($Object, $Label, FirePHP_WARN);
203
+ }
204
+
205
+ /**
206
+ * Log object with label to firebug console
207
+ *
208
+ * @see FirePHP::ERROR
209
+ * @param mixes $Object
210
+ * @param string $Label
211
+ * @return true
212
+ */
213
+ function error($Object, $Label=null) {
214
+ return FB::send($Object, $Label, FirePHP_ERROR);
215
+ }
216
+
217
+ /**
218
+ * Dumps key and variable to firebug server panel
219
+ *
220
+ * @see FirePHP::DUMP
221
+ * @param string $Key
222
+ * @param mixed $Variable
223
+ * @return true
224
+ */
225
+ function dump($Key, $Variable) {
226
+ return FB::send($Variable, $Key, FirePHP_DUMP);
227
+ }
228
+
229
+ /**
230
+ * Log a trace in the firebug console
231
+ *
232
+ * @see FirePHP::TRACE
233
+ * @param string $Label
234
+ * @return true
235
+ */
236
+ function trace($Label) {
237
+ return FB::send($Label, FirePHP_TRACE);
238
+ }
239
+
240
+ /**
241
+ * Log a table in the firebug console
242
+ *
243
+ * @see FirePHP::TABLE
244
+ * @param string $Label
245
+ * @param string $Table
246
+ * @return true
247
+ */
248
+ function table($Label, $Table) {
249
+ return FB::send($Table, $Label, FirePHP_TABLE);
250
+ }
251
+ }
classes/libs/class-gump.php ADDED
@@ -0,0 +1,2029 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\libs;
3
+ /**
4
+ * GUMP - A fast, extensible PHP input validation class.
5
+ *
6
+ * @author Sean Nieuwoudt (http://twitter.com/SeanNieuwoudt)
7
+ * @copyright Copyright (c) 2015 wixelhq.com
8
+ *
9
+ * @link http://github.com/Wixel/GUMP
10
+ *
11
+ * @version 1.0
12
+ */
13
+ class GUMP {
14
+ // Validation rules for execution
15
+ protected $validation_rules = array();
16
+
17
+ // Filter rules for execution
18
+ protected $filter_rules = array();
19
+
20
+ // Instance attribute containing errors from last run
21
+ protected $errors = array();
22
+
23
+ // Contain readable field names that have been set manually
24
+ protected static $fields = array();
25
+
26
+ // Custom validation methods
27
+ protected static $validation_methods = array();
28
+
29
+ // Customer filter methods
30
+ protected static $filter_methods = array();
31
+
32
+ // ** ------------------------- Validation Data ------------------------------- ** //
33
+
34
+ public static $basic_tags = '<br><p><a><strong><b><i><em><img><blockquote><code><dd><dl><hr><h1><h2><h3><h4><h5><h6><label><ul><li><span><sub><sup>';
35
+
36
+ public static $en_noise_words = "about,after,all,also,an,and,another,any,are,as,at,be,because,been,before,
37
+ being,between,both,but,by,came,can,come,could,did,do,each,for,from,get,
38
+ got,has,had,he,have,her,here,him,himself,his,how,if,in,into,is,it,its,it's,like,
39
+ make,many,me,might,more,most,much,must,my,never,now,of,on,only,or,other,
40
+ our,out,over,said,same,see,should,since,some,still,such,take,than,that,
41
+ the,their,them,then,there,these,they,this,those,through,to,too,under,up,
42
+ very,was,way,we,well,were,what,where,which,while,who,with,would,you,your,a,
43
+ b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,$,1,2,3,4,5,6,7,8,9,0,_";
44
+
45
+ // field characters below will be replaced with a space.
46
+ protected $fieldCharsToRemove = array('_', '-');
47
+
48
+ // ** ------------------------- Validation Helpers ---------------------------- ** //
49
+
50
+ /**
51
+ * Shorthand method for inline validation.
52
+ *
53
+ * @param array $data The data to be validated
54
+ * @param array $validators The GUMP validators
55
+ *
56
+ * @return mixed True(boolean) or the array of error messages
57
+ */
58
+ public static function is_valid(array $data, array $validators) {
59
+ $gump = new self();
60
+
61
+ $gump->validation_rules($validators);
62
+
63
+ if ($gump->run($data) === false) {
64
+ return $gump->get_readable_errors(false);
65
+ } else {
66
+ return true;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Shorthand method for running only the data filters.
72
+ *
73
+ * @param array $data
74
+ * @param array $filters
75
+ *
76
+ * @return mixed
77
+ */
78
+ public static function filter_input(array $data, array $filters) {
79
+ $gump = new self();
80
+
81
+ return $gump->filter($data, $filters);
82
+ }
83
+
84
+ /**
85
+ * Magic method to generate the validation error messages.
86
+ *
87
+ * @return string
88
+ */
89
+ public function __toString() {
90
+ return $this->get_readable_errors(true);
91
+ }
92
+
93
+ /**
94
+ * Perform XSS clean to prevent cross site scripting.
95
+ *
96
+ * @static
97
+ *
98
+ * @param array $data
99
+ *
100
+ * @return array
101
+ */
102
+ public static function xss_clean(array $data) {
103
+ foreach ($data as $k => $v) {
104
+ $data[$k] = filter_var($v, FILTER_SANITIZE_STRING);
105
+ }
106
+
107
+ return $data;
108
+ }
109
+
110
+ /**
111
+ * Adds a custom validation rule using a callback function.
112
+ *
113
+ * @param string $rule
114
+ * @param callable $callback
115
+ *
116
+ * @return bool
117
+ *
118
+ * @throws Exception
119
+ */
120
+ public static function add_validator($rule, $callback) {
121
+ $method = 'validate_' . $rule;
122
+
123
+ if (method_exists(__CLASS__, $method) || isset(self::$validation_methods[$rule])) {
124
+ throw new Exception("Validator rule '$rule' already exists.");
125
+ }
126
+
127
+ self::$validation_methods[$rule] = $callback;
128
+
129
+ return true;
130
+ }
131
+
132
+ /**
133
+ * Adds a custom filter using a callback function.
134
+ *
135
+ * @param string $rule
136
+ * @param callable $callback
137
+ *
138
+ * @return bool
139
+ *
140
+ * @throws Exception
141
+ */
142
+ public static function add_filter($rule, $callback) {
143
+ $method = 'filter_' . $rule;
144
+
145
+ if (method_exists(__CLASS__, $method) || isset(self::$filter_methods[$rule])) {
146
+ throw new Exception("Filter rule '$rule' already exists.");
147
+ }
148
+
149
+ self::$filter_methods[$rule] = $callback;
150
+
151
+ return true;
152
+ }
153
+
154
+ /**
155
+ * Helper method to extract an element from an array safely
156
+ *
157
+ * @param mixed $key
158
+ * @param array $array
159
+ * @param mixed $default
160
+ *
161
+ * @return mixed
162
+ */
163
+ public static function field($key, array $array, $default = null) {
164
+ if (!is_array($array)) {
165
+ return null;
166
+ }
167
+
168
+ if (isset($array[$key])) {
169
+ return $array[$key];
170
+ } else {
171
+ return $default;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Getter/Setter for the validation rules.
177
+ *
178
+ * @param array $rules
179
+ *
180
+ * @return array
181
+ */
182
+ public function validation_rules(array $rules = array()) {
183
+ if (empty($rules)) {
184
+ return $this->validation_rules;
185
+ }
186
+
187
+ $this->validation_rules = $rules;
188
+ }
189
+
190
+ /**
191
+ * Getter/Setter for the filter rules.
192
+ *
193
+ * @param array $rules
194
+ *
195
+ * @return array
196
+ */
197
+ public function filter_rules(array $rules = array()) {
198
+ if (empty($rules)) {
199
+ return $this->filter_rules;
200
+ }
201
+
202
+ $this->filter_rules = $rules;
203
+ }
204
+
205
+ /**
206
+ * Run the filtering and validation after each other.
207
+ *
208
+ * @param array $data
209
+ * @param bool $check_fields
210
+ *
211
+ * @return array
212
+ *
213
+ * @throws Exception
214
+ */
215
+ public function run(array $data, $check_fields = false) {
216
+ $data = $this->filter($data, $this->filter_rules());
217
+
218
+ $validated = $this->validate(
219
+ $data, $this->validation_rules()
220
+ );
221
+
222
+ if ($check_fields === true) {
223
+ $this->check_fields($data);
224
+ }
225
+
226
+ if ($validated !== true) {
227
+ return false;
228
+ }
229
+
230
+ return $data;
231
+ }
232
+
233
+ /**
234
+ * Ensure that the field counts match the validation rule counts.
235
+ *
236
+ * @param array $data
237
+ */
238
+ private function check_fields(array $data) {
239
+ $ruleset = $this->validation_rules();
240
+ $mismatch = array_diff_key($data, $ruleset);
241
+ $fields = array_keys($mismatch);
242
+
243
+ foreach ($fields as $field) {
244
+ $this->errors[] = array(
245
+ 'field' => $field,
246
+ 'value' => $data[$field],
247
+ 'rule' => 'mismatch',
248
+ 'param' => null,
249
+ );
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Sanitize the input data.
255
+ *
256
+ * @param array $input
257
+ * @param null $fields
258
+ * @param bool $utf8_encode
259
+ *
260
+ * @return array
261
+ */
262
+ public function sanitize(array $input, array $fields = array(), $utf8_encode = true) {
263
+ $magic_quotes = (bool)get_magic_quotes_gpc();
264
+
265
+ if (empty($fields)) {
266
+ $fields = array_keys($input);
267
+ }
268
+
269
+ $return = array();
270
+
271
+ foreach ($fields as $field) {
272
+ if (!isset($input[$field])) {
273
+ continue;
274
+ } else {
275
+ $value = $input[$field];
276
+ if (is_array($value)) {
277
+ $value = null;
278
+ }
279
+ if (is_string($value)) {
280
+ if ($magic_quotes === true) {
281
+ $value = stripslashes($value);
282
+ }
283
+
284
+ if (strpos($value, "\r") !== false) {
285
+ $value = trim($value);
286
+ }
287
+
288
+ if (function_exists('iconv') && function_exists('mb_detect_encoding') && $utf8_encode) {
289
+ $current_encoding = mb_detect_encoding($value);
290
+
291
+ if ($current_encoding != 'UTF-8' && $current_encoding != 'UTF-16') {
292
+ $value = iconv($current_encoding, 'UTF-8', $value);
293
+ }
294
+ }
295
+
296
+ $value = filter_var($value, FILTER_SANITIZE_STRING);
297
+ }
298
+
299
+ $return[$field] = $value;
300
+ }
301
+ }
302
+
303
+ return $return;
304
+ }
305
+
306
+ /**
307
+ * Return the error array from the last validation run.
308
+ *
309
+ * @return array
310
+ */
311
+ public function errors() {
312
+ return $this->errors;
313
+ }
314
+
315
+ /**
316
+ * Perform data validation against the provided ruleset.
317
+ *
318
+ * @param mixed $input
319
+ * @param array $ruleset
320
+ *
321
+ * @return mixed
322
+ *
323
+ * @throws Exception
324
+ */
325
+ public function validate(array $input, array $ruleset) {
326
+ $this->errors = array();
327
+
328
+ foreach ($ruleset as $field => $rules) {
329
+
330
+ $rules = explode('|', $rules);
331
+
332
+ if (in_array('required', $rules) || (isset($input[$field]) && !is_array($input[$field]))) {
333
+ foreach ($rules as $rule) {
334
+ $method = null;
335
+ $param = null;
336
+
337
+ // Check if we have rule parameters
338
+ if (strstr($rule, ',') !== false) {
339
+ $rule = explode(',', $rule);
340
+ $method = 'validate_' . $rule[0];
341
+ $param = $rule[1];
342
+ $rule = $rule[0];
343
+ } else {
344
+ $method = 'validate_' . $rule;
345
+ }
346
+
347
+ //self::$validation_methods[$rule] = $callback;
348
+
349
+ if (is_callable(array($this, $method))) {
350
+ $result = $this->$method(
351
+ $field, $input, $param
352
+ );
353
+
354
+ if (is_array($result)) {
355
+ $this->errors[] = $result;
356
+ }
357
+ } elseif (isset(self::$validation_methods[$rule])) {
358
+
359
+ $result = call_user_func(self::$validation_methods[$rule], $field, $input, $param);
360
+
361
+ if ($result === false) {
362
+ $this->errors[] = array(
363
+ 'field' => $field,
364
+ 'value' => $input,
365
+ 'rule' => self::$validation_methods[$rule],
366
+ 'param' => $param,
367
+ );
368
+ }
369
+
370
+ } else {
371
+ throw new Exception("Validator method '$method' does not exist.");
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ return (count($this->errors) > 0) ? $this->errors : true;
378
+ }
379
+
380
+ /**
381
+ * Overloadable method to invoke validation.
382
+ *
383
+ * @param array $input
384
+ * @param $rules
385
+ * @param $field
386
+ *
387
+ * @return bool
388
+ */
389
+ protected function shouldRunValidation(array $input, $rules, $field) {
390
+ return in_array('required', $rules) || (isset($input[$field]) && trim($input[$field]) != '');
391
+ }
392
+
393
+ /**
394
+ * Set a readable name for a specified field names.
395
+ *
396
+ * @param string $field
397
+ * @param string $readable_name
398
+ */
399
+ public static function set_field_name($field, $readable_name) {
400
+ self::$fields[$field] = $readable_name;
401
+ }
402
+
403
+ /**
404
+ * Set readable name for specified fields in an array.
405
+ *
406
+ * Usage:
407
+ *
408
+ * GUMP::set_field_names(array(
409
+ * "name" => "My Lovely Name",
410
+ * "username" => "My Beloved Username",
411
+ * ));
412
+ *
413
+ * @param array $array
414
+ */
415
+ public static function set_field_names(array $array) {
416
+ foreach ($array as $field => $readable_name) {
417
+ self::$fields[$field] = $readable_name;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Process the validation errors and return human readable error messages.
423
+ *
424
+ * @param bool $convert_to_string = false
425
+ * @param string $field_class
426
+ * @param string $error_class
427
+ *
428
+ * @return array
429
+ * @return string
430
+ */
431
+ public function get_readable_errors($convert_to_string = false, $field_class = 'gump-field', $error_class = 'gump-error-message') {
432
+ if (empty($this->errors)) {
433
+ return ($convert_to_string) ? null : array();
434
+ }
435
+
436
+ $resp = array();
437
+
438
+ foreach ($this->errors as $e) {
439
+ $field = ucwords(str_replace($this->fieldCharsToRemove, chr(32), $e['field']));
440
+ $param = $e['param'];
441
+
442
+ // Let's fetch explicit field names if they exist
443
+ if (array_key_exists($e['field'], self::$fields)) {
444
+ $field = self::$fields[$e['field']];
445
+ }
446
+
447
+
448
+ switch ($e['rule']) {
449
+ case 'mismatch' :
450
+ $resp[] = sprintf( 'There is no validation rule for <span class="%1$s">%2$s</span>', $field_class, $field );
451
+ break;
452
+ case 'validate_required' :
453
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field is required', $field_class, $field );
454
+ break;
455
+ case 'validate_valid_email':
456
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field is required to be a valid email address', $field_class, $field );
457
+ break;
458
+ case 'validate_max_len':
459
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be %d or shorter in length', $field_class, $field, $param );
460
+ break;
461
+ case 'validate_min_len':
462
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be %d or longer in length', $field_class, $field, $param );
463
+ break;
464
+ case 'validate_exact_len':
465
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be exactly %d characters in length', $field_class, $field, $param );
466
+ break;
467
+ case 'validate_alpha':
468
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain alpha characters(a-z)', $field_class, $field );
469
+ break;
470
+ case 'validate_alpha_numeric':
471
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain alpha-numeric characters', $field_class, $field );
472
+ break;
473
+ case 'validate_alpha_dash':
474
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain alpha characters &amp; dashes', $field_class, $field );
475
+ break;
476
+ case 'validate_numeric':
477
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain numeric characters', $field_class, $field );
478
+ break;
479
+ case 'validate_integer':
480
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain a numeric value', $field_class, $field );
481
+ break;
482
+ case 'validate_boolean':
483
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain a true or false value', $field_class, $field );
484
+ break;
485
+ case 'validate_float':
486
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field may only contain a float value', $field_class, $field );
487
+ break;
488
+ case 'validate_valid_url':
489
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field is required to be a valid URL', $field_class, $field );
490
+ break;
491
+ case 'validate_url_exists':
492
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> URL does not exist', $field_class, $field );
493
+ break;
494
+ case 'validate_valid_ip':
495
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to contain a valid IP address', $field_class, $field );
496
+ break;
497
+ case 'validate_valid_cc':
498
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to contain a valid credit card number', $field_class, $field );
499
+ break;
500
+ case 'validate_valid_name':
501
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to contain a valid human name', $field_class, $field );
502
+ break;
503
+ case 'validate_contains':
504
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to contain one of these values:', $field_class, $field ) . ' ' . implode(', ', $param);
505
+ break;
506
+ case 'validate_contains_list':
507
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs contain a value from its drop down list', $field_class, $field );
508
+ break;
509
+ case 'validate_doesnt_contain_list':
510
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field contains a value that is not accepted', $field_class, $field );
511
+ break;
512
+ case 'validate_street_address':
513
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be a valid street address', $field_class, $field );
514
+ break;
515
+ case 'validate_date':
516
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be a valid date', $field_class, $field );
517
+ break;
518
+ case 'validate_min_numeric':
519
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be a numeric value, equal to, or higher than', $field_class, $field ) . $param;
520
+ break;
521
+ case 'validate_max_numeric':
522
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to be a numeric value, equal to, or lower than', $field_class, $field ) . ' ' . $param;
523
+ break;
524
+ case 'validate_starts':
525
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to start with', $field_class, $field ) . ' ' . $param;
526
+ break;
527
+ case 'validate_extension':
528
+ $resp[] = sprintf( 'The <span class"%1$s">%2$s</span> field can have the following extensions', $field_class, $field ) . ' '. $param;
529
+ break;
530
+ case 'validate_required_file':
531
+ $resp[] = sprintf( 'The <span class"%1$s">%2$s</span> field is required', $field_class, $field ) .' ' . $param;
532
+ break;
533
+ case 'validate_equalsfield':
534
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field does not equal %d field', $field_class, $field, $param ) ;
535
+ break;
536
+ case 'validate_min_age':
537
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field needs to have an age greater than or equal to', $field_class, $field ) . ' '. $param;;
538
+ break;
539
+ default:
540
+ $resp[] = sprintf( 'The <span class="%1$s">%2$s</span> field is invalid', $field_class, $field );
541
+ }
542
+ }
543
+
544
+ if (!$convert_to_string) {
545
+ return $resp;
546
+ } else {
547
+ $buffer = '';
548
+ foreach ($resp as $s) {
549
+ $buffer .= "<span class=\"$error_class\">$s</span>";
550
+ }
551
+
552
+ return $buffer;
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Process the validation errors and return an array of errors with field names as keys.
558
+ *
559
+ * @param $convert_to_string
560
+ *
561
+ * @return array | null (if empty)
562
+ */
563
+ public function get_errors_array($convert_to_string = null) {
564
+ if (empty($this->errors)) {
565
+ return ($convert_to_string) ? null : array();
566
+ }
567
+
568
+ $resp = array();
569
+
570
+ foreach ($this->errors as $e) {
571
+ $field = ucwords(str_replace(array('_', '-'), chr(32), $e['field']));
572
+ $param = $e['param'];
573
+
574
+ // Let's fetch explicit field names if they exist
575
+ if (array_key_exists($e['field'], self::$fields)) {
576
+ $field = self::$fields[$e['field']];
577
+ }
578
+
579
+ switch ($e['rule']) {
580
+ case 'mismatch' :
581
+ $resp[$field] = 'There is no validation rule for' . ' ' . $field;
582
+ break;
583
+ case 'validate_required':
584
+ $resp[$field] = sprintf( 'The %s field is required', $field);
585
+ break;
586
+ case 'validate_valid_email':
587
+ $resp[$field] = sprintf( 'The %s field is required to be a valid email address', $field);
588
+ break;
589
+ case 'validate_max_len':
590
+ $resp[$field] = sprintf( 'The %s field needs to be %d or shorter in length', $field, $param);
591
+ break;
592
+ case 'validate_min_len':
593
+ $resp[$field] = sprintf( 'The %s field needs to be %d or longer in length', $field, $param);
594
+ break;
595
+ case 'validate_exact_len':
596
+ $resp[$field] = sprintf( 'The %s field needs to be exactly %d characters in length', $field, $param);
597
+ break;
598
+ case 'validate_alpha':
599
+ $resp[$field] = sprintf( 'The %s field may only contain alpha characters(a-z)', $field);
600
+ break;
601
+ case 'validate_alpha_numeric':
602
+ $resp[$field] = sprintf( 'The %s field may only contain alpha-numeric characters', $field);
603
+ break;
604
+ case 'validate_alpha_dash':
605
+ $resp[$field] = sprintf( 'The %s field may only contain alpha characters &amp; dashes', $field);
606
+ break;
607
+ case 'validate_numeric':
608
+ $resp[$field] = sprintf( 'The %s field may only contain numeric characters', $field);
609
+ break;
610
+ case 'validate_integer':
611
+ $resp[$field] = sprintf( 'The %s field may only contain a numeric value', $field);
612
+ break;
613
+ case 'validate_boolean':
614
+ $resp[$field] = sprintf( 'The %s field may only contain a true or false value', $field);
615
+ break;
616
+ case 'validate_float':
617
+ $resp[$field] = sprintf( 'The %s field may only contain a float value', $field);
618
+ break;
619
+ case 'validate_valid_url':
620
+ $resp[$field] = sprintf( 'The %s field is required to be a valid URL', $field);
621
+ break;
622
+ case 'validate_url_exists':
623
+ $resp[$field] = sprintf( 'The %s URL does not exist', $field);
624
+ break;
625
+ case 'validate_valid_ip':
626
+ $resp[$field] = sprintf( 'The %s field needs to contain a valid IP address', $field);
627
+ break;
628
+ case 'validate_valid_cc':
629
+ $resp[$field] = sprintf( 'The %s field needs to contain a valid credit card number', $field);
630
+ break;
631
+ case 'validate_valid_name':
632
+ $resp[$field] = sprintf( 'The %s field needs to contain a valid human name', $field);
633
+ break;
634
+ case 'validate_contains':
635
+ $resp[$field] = sprintf( 'The %s field needs to contain one of these values:', $field) . ' ' . implode(', ', $param);
636
+ break;
637
+ case 'validate_street_address':
638
+ $resp[$field] = sprintf( 'The %s field needs to be a valid street address', $field);
639
+ break;
640
+ case 'validate_date':
641
+ $resp[$field] = sprintf( 'The %s field needs to be a valid date', $field);
642
+ break;
643
+ case 'validate_min_numeric':
644
+ $resp[$field] = sprintf( 'The %s field needs to be a numeric value, equal to, or higher than %d', $field, $param);
645
+ break;
646
+ case 'validate_max_numeric':
647
+ $resp[$field] = sprintf( 'The %s field needs to be a numeric value, equal to, or lower than %d', $field, $param);
648
+ break;
649
+ case 'validate_min_age':
650
+ $resp[$field] = sprintf( 'The %s field needs to have an age greater than or equal to %d', $field, $param);
651
+ break;
652
+ default:
653
+ $resp[$field] = sprintf( 'The %s field is invalid', $field );
654
+ }
655
+ }
656
+
657
+ return $resp;
658
+ }
659
+
660
+
661
+ /**
662
+ * Filter the input data according to the specified filter set.
663
+ *
664
+ * @param mixed $input
665
+ * @param array $filterset
666
+ *
667
+ * @throws Exception
668
+ *
669
+ * @return mixed
670
+ *
671
+ * @throws Exception
672
+ */
673
+ public function filter(array $input, array $filterset) {
674
+ foreach ($filterset as $field => $filters) {
675
+ if (!array_key_exists($field, $input)) {
676
+ continue;
677
+ }
678
+
679
+ $filters = explode('|', $filters);
680
+
681
+ foreach ($filters as $filter) {
682
+ $params = null;
683
+
684
+ if (strstr($filter, ',') !== false) {
685
+ $filter = explode(',', $filter);
686
+
687
+ $params = array_slice($filter, 1, count($filter) - 1);
688
+
689
+ $filter = $filter[0];
690
+ }
691
+
692
+ if (is_callable(array($this, 'filter_' . $filter))) {
693
+ $method = 'filter_' . $filter;
694
+ $input[$field] = $this->$method($input[$field], $params);
695
+ } elseif (function_exists($filter)) {
696
+ $input[$field] = $filter($input[$field]);
697
+ } elseif (isset(self::$filter_methods[$filter])) {
698
+ $input[$field] = call_user_func(self::$filter_methods[$filter], $input[$field], $params);
699
+ } else {
700
+ throw new Exception("Filter method '$filter' does not exist.");
701
+ }
702
+ }
703
+ }
704
+
705
+ return $input;
706
+ }
707
+
708
+ // ** ------------------------- Filters --------------------------------------- ** //
709
+
710
+ /**
711
+ * Replace noise words in a string (http://tax.cchgroup.com/help/Avoiding_noise_words_in_your_search.htm).
712
+ *
713
+ * Usage: '<index>' => 'noise_words'
714
+ *
715
+ * @param string $value
716
+ * @param array $params
717
+ *
718
+ * @return string
719
+ */
720
+ protected function filter_noise_words($value, $params = null) {
721
+ $value = preg_replace('/\s\s+/u', chr(32), $value);
722
+
723
+ $value = " $value ";
724
+
725
+ $words = explode(',', self::$en_noise_words);
726
+
727
+ foreach ($words as $word) {
728
+ $word = trim($word);
729
+
730
+ $word = " $word "; // Normalize
731
+
732
+ if (stripos($value, $word) !== false) {
733
+ $value = str_ireplace($word, chr(32), $value);
734
+ }
735
+ }
736
+
737
+ return trim($value);
738
+ }
739
+
740
+ /**
741
+ * Remove all known punctuation from a string.
742
+ *
743
+ * Usage: '<index>' => 'rmpunctuataion'
744
+ *
745
+ * @param string $value
746
+ * @param array $params
747
+ *
748
+ * @return string
749
+ */
750
+ protected function filter_rmpunctuation($value, $params = null) {
751
+ return preg_replace("/(?![.=$'€%-])\p{P}/u", '', $value);
752
+ }
753
+
754
+ /**
755
+ * Translate an input string to a desired language [DEPRECIATED].
756
+ *
757
+ * Any ISO 639-1 2 character language code may be used
758
+ *
759
+ * See: http://www.science.co.il/language/Codes.asp?s=code2
760
+ *
761
+ * @param string $value
762
+ * @param array $params
763
+ *
764
+ * @return string
765
+ */
766
+ /*
767
+ protected function filter_translate($value, $params = NULL)
768
+ {
769
+ $input_lang = 'en';
770
+ $output_lang = 'en';
771
+
772
+ if(is_null($params))
773
+ {
774
+ return $value;
775
+ }
776
+
777
+ switch(count($params))
778
+ {
779
+ case 1:
780
+ $input_lang = $params[0];
781
+ break;
782
+ case 2:
783
+ $input_lang = $params[0];
784
+ $output_lang = $params[1];
785
+ break;
786
+ }
787
+
788
+ $text = urlencode($value);
789
+
790
+ $translation = file_get_contents(
791
+ "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q={$text}&langpair={$input_lang}|{$output_lang}"
792
+ );
793
+
794
+ $json = json_decode($translation, true);
795
+
796
+ if($json['responseStatus'] != 200)
797
+ {
798
+ return $value;
799
+ }
800
+ else
801
+ {
802
+ return $json['responseData']['translatedText'];
803
+ }
804
+ }
805
+ */
806
+
807
+ /**
808
+ * Sanitize the string by removing any script tags.
809
+ *
810
+ * Usage: '<index>' => 'sanitize_string'
811
+ *
812
+ * @param string $value
813
+ * @param array $params
814
+ *
815
+ * @return string
816
+ */
817
+ protected function filter_sanitize_string($value, $params = null) {
818
+ return filter_var($value, FILTER_SANITIZE_STRING);
819
+ }
820
+
821
+ /**
822
+ * Sanitize the string by urlencoding characters.
823
+ *
824
+ * Usage: '<index>' => 'urlencode'
825
+ *
826
+ * @param string $value
827
+ * @param array $params
828
+ *
829
+ * @return string
830
+ */
831
+ protected function filter_urlencode($value, $params = null) {
832
+ return filter_var($value, FILTER_SANITIZE_ENCODED);
833
+ }
834
+
835
+ /**
836
+ * Sanitize the string by converting HTML characters to their HTML entities.
837
+ *
838
+ * Usage: '<index>' => 'htmlencode'
839
+ *
840
+ * @param string $value
841
+ * @param array $params
842
+ *
843
+ * @return string
844
+ */
845
+ protected function filter_htmlencode($value, $params = null) {
846
+ return filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
847
+ }
848
+
849
+ /**
850
+ * Sanitize the string by removing illegal characters from emails.
851
+ *
852
+ * Usage: '<index>' => 'sanitize_email'
853
+ *
854
+ * @param string $value
855
+ * @param array $params
856
+ *
857
+ * @return string
858
+ */
859
+ protected function filter_sanitize_email($value, $params = null) {
860
+ return filter_var($value, FILTER_SANITIZE_EMAIL);
861
+ }
862
+
863
+ /**
864
+ * Sanitize the string by removing illegal characters from numbers.
865
+ *
866
+ * @param string $value
867
+ * @param array $params
868
+ *
869
+ * @return string
870
+ */
871
+ protected function filter_sanitize_numbers($value, $params = null) {
872
+ return filter_var($value, FILTER_SANITIZE_NUMBER_INT);
873
+ }
874
+
875
+ /**
876
+ * Filter out all HTML tags except the defined basic tags.
877
+ *
878
+ * @param string $value
879
+ * @param array $params
880
+ *
881
+ * @return string
882
+ */
883
+ protected function filter_basic_tags($value, $params = null) {
884
+ return strip_tags($value, self::$basic_tags);
885
+ }
886
+
887
+ /**
888
+ * Convert the provided numeric value to a whole number.
889
+ *
890
+ * @param string $value
891
+ * @param array $params
892
+ *
893
+ * @return string
894
+ */
895
+ protected function filter_whole_number($value, $params = null) {
896
+ return intval($value);
897
+ }
898
+
899
+ // ** ------------------------- Validators ------------------------------------ ** //
900
+
901
+
902
+ /**
903
+ * Verify that a value is contained within the pre-defined value set.
904
+ *
905
+ * Usage: '<index>' => 'contains,value value value'
906
+ *
907
+ * @param string $field
908
+ * @param array $input
909
+ * @param null $param
910
+ *
911
+ * @return mixed
912
+ */
913
+ protected function validate_contains($field, $input, $param = null) {
914
+ if (!isset($input[$field])) {
915
+ return;
916
+ }
917
+
918
+ $param = trim(strtolower($param));
919
+
920
+ $value = trim(strtolower($input[$field]));
921
+
922
+ if (preg_match_all('#\'(.+?)\'#', $param, $matches, PREG_PATTERN_ORDER)) {
923
+ $param = $matches[1];
924
+ } else {
925
+ $param = explode(chr(32), $param);
926
+ }
927
+
928
+ if (in_array($value, $param)) { // valid, return nothing
929
+ return;
930
+ }
931
+
932
+ return array(
933
+ 'field' => $field,
934
+ 'value' => $value,
935
+ 'rule' => __FUNCTION__,
936
+ 'param' => $param,
937
+ );
938
+ }
939
+
940
+ /**
941
+ * Verify that a value is contained within the pre-defined value set.
942
+ * OUTPUT: will NOT show the list of values.
943
+ *
944
+ * Usage: '<index>' => 'contains_list,value;value;value'
945
+ *
946
+ * @param string $field
947
+ * @param array $input
948
+ *
949
+ * @return mixed
950
+ */
951
+ protected function validate_contains_list($field, $input, $param = null) {
952
+ $param = trim(strtolower($param));
953
+
954
+ $value = trim(strtolower($input[$field]));
955
+
956
+ $param = explode(';', $param);
957
+
958
+ // consider: in_array(strtolower($value), array_map('strtolower', $param)
959
+
960
+ if (in_array($value, $param)) { // valid, return nothing
961
+ return;
962
+ } else {
963
+ return array(
964
+ 'field' => $field,
965
+ 'value' => $value,
966
+ 'rule' => __FUNCTION__,
967
+ 'param' => $param,
968
+ );
969
+ }
970
+ }
971
+
972
+ /**
973
+ * Verify that a value is NOT contained within the pre-defined value set.
974
+ * OUTPUT: will NOT show the list of values.
975
+ *
976
+ * Usage: '<index>' => 'doesnt_contain_list,value;value;value'
977
+ *
978
+ * @param string $field
979
+ * @param array $input
980
+ *
981
+ * @return mixed
982
+ */
983
+ protected function validate_doesnt_contain_list($field, $input, $param = null) {
984
+ $param = trim(strtolower($param));
985
+
986
+ $value = trim(strtolower($input[$field]));
987
+
988
+ $param = explode(';', $param);
989
+
990
+ if (!in_array($value, $param)) { // valid, return nothing
991
+ return;
992
+ } else {
993
+ return array(
994
+ 'field' => $field,
995
+ 'value' => $value,
996
+ 'rule' => __FUNCTION__,
997
+ 'param' => $param,
998
+ );
999
+ }
1000
+ }
1001
+
1002
+ /**
1003
+ * Check if the specified key is present and not empty.
1004
+ *
1005
+ * Usage: '<index>' => 'required'
1006
+ *
1007
+ * @param string $field
1008
+ * @param array $input
1009
+ * @param null $param
1010
+ *
1011
+ * @return mixed
1012
+ */
1013
+ protected function validate_required($field, $input, $param = null) {
1014
+ if (isset($input[$field]) && ($input[$field] === false || $input[$field] === 0 || $input[$field] === 0.0 || $input[$field] === '0' || !empty($input[$field]))) {
1015
+ return;
1016
+ }
1017
+
1018
+ return array(
1019
+ 'field' => $field,
1020
+ 'value' => null,
1021
+ 'rule' => __FUNCTION__,
1022
+ 'param' => $param,
1023
+ );
1024
+ }
1025
+
1026
+ /**
1027
+ * Determine if the provided email is valid.
1028
+ *
1029
+ * Usage: '<index>' => 'valid_email'
1030
+ *
1031
+ * @param string $field
1032
+ * @param array $input
1033
+ * @param null $param
1034
+ *
1035
+ * @return mixed
1036
+ */
1037
+ protected function validate_valid_email($field, $input, $param = null) {
1038
+ if (!isset($input[$field]) || empty($input[$field])) {
1039
+ return;
1040
+ }
1041
+
1042
+ if (!filter_var($input[$field], FILTER_VALIDATE_EMAIL)) {
1043
+ return array(
1044
+ 'field' => $field,
1045
+ 'value' => $input[$field],
1046
+ 'rule' => __FUNCTION__,
1047
+ 'param' => $param,
1048
+ );
1049
+ }
1050
+ }
1051
+
1052
+ /**
1053
+ * Determine if the provided value length is less or equal to a specific value.
1054
+ *
1055
+ * Usage: '<index>' => 'max_len,240'
1056
+ *
1057
+ * @param string $field
1058
+ * @param array $input
1059
+ * @param null $param
1060
+ *
1061
+ * @return mixed
1062
+ */
1063
+ protected function validate_max_len($field, $input, $param = null) {
1064
+ if (!isset($input[$field])) {
1065
+ return;
1066
+ }
1067
+
1068
+ if (function_exists('mb_strlen')) {
1069
+ if (mb_strlen($input[$field]) <= (int)$param) {
1070
+ return;
1071
+ }
1072
+ } else {
1073
+ if (strlen($input[$field]) <= (int)$param) {
1074
+ return;
1075
+ }
1076
+ }
1077
+
1078
+ return array(
1079
+ 'field' => $field,
1080
+ 'value' => $input[$field],
1081
+ 'rule' => __FUNCTION__,
1082
+ 'param' => $param,
1083
+ );
1084
+ }
1085
+
1086
+ /**
1087
+ * Determine if the provided value length is more or equal to a specific value.
1088
+ *
1089
+ * Usage: '<index>' => 'min_len,4'
1090
+ *
1091
+ * @param string $field
1092
+ * @param array $input
1093
+ * @param null $param
1094
+ *
1095
+ * @return mixed
1096
+ */
1097
+ protected function validate_min_len($field, $input, $param = null) {
1098
+ if (!isset($input[$field])) {
1099
+ return;
1100
+ }
1101
+
1102
+ if (function_exists('mb_strlen')) {
1103
+ if (mb_strlen($input[$field]) >= (int)$param) {
1104
+ return;
1105
+ }
1106
+ } else {
1107
+ if (strlen($input[$field]) >= (int)$param) {
1108
+ return;
1109
+ }
1110
+ }
1111
+
1112
+ return array(
1113
+ 'field' => $field,
1114
+ 'value' => $input[$field],
1115
+ 'rule' => __FUNCTION__,
1116
+ 'param' => $param,
1117
+ );
1118
+ }
1119
+
1120
+ /**
1121
+ * Determine if the provided value length matches a specific value.
1122
+ *
1123
+ * Usage: '<index>' => 'exact_len,5'
1124
+ *
1125
+ * @param string $field
1126
+ * @param array $input
1127
+ * @param null $param
1128
+ *
1129
+ * @return mixed
1130
+ */
1131
+ protected function validate_exact_len($field, $input, $param = null) {
1132
+ if (!isset($input[$field])) {
1133
+ return;
1134
+ }
1135
+
1136
+ if (function_exists('mb_strlen')) {
1137
+ if (mb_strlen($input[$field]) == (int)$param) {
1138
+ return;
1139
+ }
1140
+ } else {
1141
+ if (strlen($input[$field]) == (int)$param) {
1142
+ return;
1143
+ }
1144
+ }
1145
+
1146
+ return array(
1147
+ 'field' => $field,
1148
+ 'value' => $input[$field],
1149
+ 'rule' => __FUNCTION__,
1150
+ 'param' => $param,
1151
+ );
1152
+ }
1153
+
1154
+ /**
1155
+ * Determine if the provided value contains only alpha characters.
1156
+ *
1157
+ * Usage: '<index>' => 'alpha'
1158
+ *
1159
+ * @param string $field
1160
+ * @param array $input
1161
+ * @param null $param
1162
+ *
1163
+ * @return mixed
1164
+ */
1165
+ protected function validate_alpha($field, $input, $param = null) {
1166
+ if (!isset($input[$field]) || empty($input[$field])) {
1167
+ return;
1168
+ }
1169
+
1170
+ if (!preg_match('/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$/i', $input[$field]) !== false) {
1171
+ return array(
1172
+ 'field' => $field,
1173
+ 'value' => $input[$field],
1174
+ 'rule' => __FUNCTION__,
1175
+ 'param' => $param,
1176
+ );
1177
+ }
1178
+ }
1179
+
1180
+ /**
1181
+ * Determine if the provided value contains only alpha-numeric characters.
1182
+ *
1183
+ * Usage: '<index>' => 'alpha_numeric'
1184
+ *
1185
+ * @param string $field
1186
+ * @param array $input
1187
+ * @param null $param
1188
+ *
1189
+ * @return mixed
1190
+ */
1191
+ protected function validate_alpha_numeric($field, $input, $param = null) {
1192
+ if (!isset($input[$field]) || empty($input[$field])) {
1193
+ return;
1194
+ }
1195
+
1196
+ if (!preg_match('/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ])+$/i', $input[$field]) !== false) {
1197
+ return array(
1198
+ 'field' => $field,
1199
+ 'value' => $input[$field],
1200
+ 'rule' => __FUNCTION__,
1201
+ 'param' => $param,
1202
+ );
1203
+ }
1204
+ }
1205
+
1206
+ /**
1207
+ * Determine if the provided value contains only alpha characters with dashed and underscores.
1208
+ *
1209
+ * Usage: '<index>' => 'alpha_dash'
1210
+ *
1211
+ * @param string $field
1212
+ * @param array $input
1213
+ * @param null $param
1214
+ *
1215
+ * @return mixed
1216
+ */
1217
+ protected function validate_alpha_dash($field, $input, $param = null) {
1218
+ if (!isset($input[$field]) || empty($input[$field])) {
1219
+ return;
1220
+ }
1221
+
1222
+ if (!preg_match('/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ_-])+$/i', $input[$field]) !== false) {
1223
+ return array(
1224
+ 'field' => $field,
1225
+ 'value' => $input[$field],
1226
+ 'rule' => __FUNCTION__,
1227
+ 'param' => $param,
1228
+ );
1229
+ }
1230
+ }
1231
+
1232
+ /**
1233
+ * Determine if the provided value contains only alpha numeric characters with spaces.
1234
+ *
1235
+ * Usage: '<index>' => 'alpha_space'
1236
+ *
1237
+ * @param string $field
1238
+ * @param array $input
1239
+ * @param null $param
1240
+ *
1241
+ * @return mixed
1242
+ */
1243
+ protected function validate_alpha_space($field, $input, $param = null) {
1244
+ if (!isset($input[$field]) || empty($input[$field])) {
1245
+ return;
1246
+ }
1247
+
1248
+ if (!preg_match("/^([a-z0-9ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ\s])+$/i", $input[$field]) !== false) {
1249
+ return array(
1250
+ 'field' => $field,
1251
+ 'value' => $input[$field],
1252
+ 'rule' => __FUNCTION__,
1253
+ 'param' => $param,
1254
+ );
1255
+ }
1256
+ }
1257
+
1258
+ /**
1259
+ * Determine if the provided value is a valid number or numeric string.
1260
+ *
1261
+ * Usage: '<index>' => 'numeric'
1262
+ *
1263
+ * @param string $field
1264
+ * @param array $input
1265
+ * @param null $param
1266
+ *
1267
+ * @return mixed
1268
+ */
1269
+ protected function validate_numeric($field, $input, $param = null) {
1270
+ if (!isset($input[$field]) || empty($input[$field])) {
1271
+ return;
1272
+ }
1273
+
1274
+ if (!is_numeric($input[$field])) {
1275
+ return array(
1276
+ 'field' => $field,
1277
+ 'value' => $input[$field],
1278
+ 'rule' => __FUNCTION__,
1279
+ 'param' => $param,
1280
+ );
1281
+ }
1282
+ }
1283
+
1284
+ /**
1285
+ * Determine if the provided value is a valid integer.
1286
+ *
1287
+ * Usage: '<index>' => 'integer'
1288
+ *
1289
+ * @param string $field
1290
+ * @param array $input
1291
+ * @param null $param
1292
+ *
1293
+ * @return mixed
1294
+ */
1295
+ protected function validate_integer($field, $input, $param = null) {
1296
+ if (!isset($input[$field]) || empty($input[$field])) {
1297
+ return;
1298
+ }
1299
+
1300
+ if (filter_var($input[$field], FILTER_VALIDATE_INT) === false) {
1301
+ return array(
1302
+ 'field' => $field,
1303
+ 'value' => $input[$field],
1304
+ 'rule' => __FUNCTION__,
1305
+ 'param' => $param,
1306
+ );
1307
+ }
1308
+ }
1309
+
1310
+ /**
1311
+ * Determine if the provided value is a PHP accepted boolean.
1312
+ *
1313
+ * Usage: '<index>' => 'boolean'
1314
+ *
1315
+ * @param string $field
1316
+ * @param array $input
1317
+ * @param null $param
1318
+ *
1319
+ * @return mixed
1320
+ */
1321
+ protected function validate_boolean($field, $input, $param = null) {
1322
+ if (!isset($input[$field]) || empty($input[$field]) && $input[$field] !== 0) {
1323
+ return;
1324
+ }
1325
+
1326
+ if ($input[$field] === true || $input[$field] === false) {
1327
+ return;
1328
+ }
1329
+
1330
+ return array(
1331
+ 'field' => $field,
1332
+ 'value' => $input[$field],
1333
+ 'rule' => __FUNCTION__,
1334
+ 'param' => $param,
1335
+ );
1336
+ }
1337
+
1338
+ /**
1339
+ * Determine if the provided value is a valid float.
1340
+ *
1341
+ * Usage: '<index>' => 'float'
1342
+ *
1343
+ * @param string $field
1344
+ * @param array $input
1345
+ * @param null $param
1346
+ *
1347
+ * @return mixed
1348
+ */
1349
+ protected function validate_float($field, $input, $param = null) {
1350
+ if (!isset($input[$field]) || empty($input[$field])) {
1351
+ return;
1352
+ }
1353
+
1354
+ if (filter_var($input[$field], FILTER_VALIDATE_FLOAT) === false) {
1355
+ return array(
1356
+ 'field' => $field,
1357
+ 'value' => $input[$field],
1358
+ 'rule' => __FUNCTION__,
1359
+ 'param' => $param,
1360
+ );
1361
+ }
1362
+ }
1363
+
1364
+ /**
1365
+ * Determine if the provided value is a valid URL.
1366
+ *
1367
+ * Usage: '<index>' => 'valid_url'
1368
+ *
1369
+ * @param string $field
1370
+ * @param array $input
1371
+ * @param null $param
1372
+ *
1373
+ * @return mixed
1374
+ */
1375
+ protected function validate_valid_url($field, $input, $param = null) {
1376
+ if (!isset($input[$field]) || empty($input[$field])) {
1377
+ return;
1378
+ }
1379
+
1380
+ if (!filter_var($input[$field], FILTER_VALIDATE_URL)) {
1381
+ return array(
1382
+ 'field' => $field,
1383
+ 'value' => $input[$field],
1384
+ 'rule' => __FUNCTION__,
1385
+ 'param' => $param,
1386
+ );
1387
+ }
1388
+ }
1389
+
1390
+ /**
1391
+ * Determine if a URL exists & is accessible.
1392
+ *
1393
+ * Usage: '<index>' => 'url_exists'
1394
+ *
1395
+ * @param string $field
1396
+ * @param array $input
1397
+ * @param null $param
1398
+ *
1399
+ * @return mixed
1400
+ */
1401
+ protected function validate_url_exists($field, $input, $param = null) {
1402
+ if (!isset($input[$field]) || empty($input[$field])) {
1403
+ return;
1404
+ }
1405
+
1406
+ $url = parse_url(strtolower($input[$field]));
1407
+
1408
+ if (isset($url['host'])) {
1409
+ $url = $url['host'];
1410
+ }
1411
+
1412
+ if (function_exists('checkdnsrr')) {
1413
+ if (checkdnsrr($url) === false) {
1414
+ return array(
1415
+ 'field' => $field,
1416
+ 'value' => $input[$field],
1417
+ 'rule' => __FUNCTION__,
1418
+ 'param' => $param,
1419
+ );
1420
+ }
1421
+ } else {
1422
+ if (gethostbyname($url) == $url) {
1423
+ return array(
1424
+ 'field' => $field,
1425
+ 'value' => $input[$field],
1426
+ 'rule' => __FUNCTION__,
1427
+ 'param' => $param,
1428
+ );
1429
+ }
1430
+ }
1431
+ }
1432
+
1433
+ /**
1434
+ * Determine if the provided value is a valid IP address.
1435
+ *
1436
+ * Usage: '<index>' => 'valid_ip'
1437
+ *
1438
+ * @param string $field
1439
+ * @param array $input
1440
+ *
1441
+ * @return mixed
1442
+ */
1443
+ protected function validate_valid_ip($field, $input, $param = null) {
1444
+ if (!isset($input[$field]) || empty($input[$field])) {
1445
+ return;
1446
+ }
1447
+
1448
+ if (!filter_var($input[$field], FILTER_VALIDATE_IP) !== false) {
1449
+ return array(
1450
+ 'field' => $field,
1451
+ 'value' => $input[$field],
1452
+ 'rule' => __FUNCTION__,
1453
+ 'param' => $param,
1454
+ );
1455
+ }
1456
+ }
1457
+
1458
+ /**
1459
+ * Determine if the provided value is a valid IPv4 address.
1460
+ *
1461
+ * Usage: '<index>' => 'valid_ipv4'
1462
+ *
1463
+ * @param string $field
1464
+ * @param array $input
1465
+ *
1466
+ * @return mixed
1467
+ *
1468
+ * @see http://pastebin.com/UvUPPYK0
1469
+ */
1470
+
1471
+ /*
1472
+ * What about private networks? http://en.wikipedia.org/wiki/Private_network
1473
+ * What about loop-back address? 127.0.0.1
1474
+ */
1475
+ protected function validate_valid_ipv4($field, $input, $param = null) {
1476
+ if (!isset($input[$field]) || empty($input[$field])) {
1477
+ return;
1478
+ }
1479
+
1480
+ if (!filter_var($input[$field], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
1481
+ // removed !== FALSE
1482
+
1483
+ return array(
1484
+ 'field' => $field,
1485
+ 'value' => $input[$field],
1486
+ 'rule' => __FUNCTION__,
1487
+ 'param' => $param,
1488
+ );
1489
+ }
1490
+ }
1491
+
1492
+ /**
1493
+ * Determine if the provided value is a valid IPv6 address.
1494
+ *
1495
+ * Usage: '<index>' => 'valid_ipv6'
1496
+ *
1497
+ * @param string $field
1498
+ * @param array $input
1499
+ *
1500
+ * @return mixed
1501
+ */
1502
+ protected function validate_valid_ipv6($field, $input, $param = null) {
1503
+ if (!isset($input[$field]) || empty($input[$field])) {
1504
+ return;
1505
+ }
1506
+
1507
+ if (!filter_var($input[$field], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
1508
+ return array(
1509
+ 'field' => $field,
1510
+ 'value' => $input[$field],
1511
+ 'rule' => __FUNCTION__,
1512
+ 'param' => $param,
1513
+ );
1514
+ }
1515
+ }
1516
+
1517
+ /**
1518
+ * Determine if the input is a valid credit card number.
1519
+ *
1520
+ * See: http://stackoverflow.com/questions/174730/what-is-the-best-way-to-validate-a-credit-card-in-php
1521
+ * Usage: '<index>' => 'valid_cc'
1522
+ *
1523
+ * @param string $field
1524
+ * @param array $input
1525
+ *
1526
+ * @return mixed
1527
+ */
1528
+ protected function validate_valid_cc($field, $input, $param = null) {
1529
+ if (!isset($input[$field]) || empty($input[$field])) {
1530
+ return;
1531
+ }
1532
+
1533
+ $number = preg_replace('/\D/', '', $input[$field]);
1534
+
1535
+ if (function_exists('mb_strlen')) {
1536
+ $number_length = mb_strlen($number);
1537
+ } else {
1538
+ $number_length = strlen($number);
1539
+ }
1540
+
1541
+ $parity = $number_length % 2;
1542
+
1543
+ $total = 0;
1544
+
1545
+ for ($i = 0; $i < $number_length; ++$i) {
1546
+ $digit = $number[$i];
1547
+
1548
+ if ($i % 2 == $parity) {
1549
+ $digit *= 2;
1550
+
1551
+ if ($digit > 9) {
1552
+ $digit -= 9;
1553
+ }
1554
+ }
1555
+
1556
+ $total += $digit;
1557
+ }
1558
+
1559
+ if ($total % 10 == 0) {
1560
+ return; // Valid
1561
+ }
1562
+
1563
+ return array(
1564
+ 'field' => $field,
1565
+ 'value' => $input[$field],
1566
+ 'rule' => __FUNCTION__,
1567
+ 'param' => $param,
1568
+ );
1569
+ }
1570
+
1571
+ /**
1572
+ * Determine if the input is a valid human name [Credits to http://github.com/ben-s].
1573
+ *
1574
+ * See: https://github.com/Wixel/GUMP/issues/5
1575
+ * Usage: '<index>' => 'valid_name'
1576
+ *
1577
+ * @param string $field
1578
+ * @param array $input
1579
+ *
1580
+ * @return mixed
1581
+ */
1582
+ protected function validate_valid_name($field, $input, $param = null) {
1583
+ if (!isset($input[$field]) || empty($input[$field])) {
1584
+ return;
1585
+ }
1586
+
1587
+ if (!preg_match("/^([a-zÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñðòóôõöùúûüýÿ '-])+$/i", $input[$field]) !== false) {
1588
+ return array(
1589
+ 'field' => $field,
1590
+ 'value' => $input[$field],
1591
+ 'rule' => __FUNCTION__,
1592
+ 'param' => $param,
1593
+ );
1594
+ }
1595
+ }
1596
+
1597
+ /**
1598
+ * Determine if the provided input is likely to be a street address using weak detection.
1599
+ *
1600
+ * Usage: '<index>' => 'street_address'
1601
+ *
1602
+ * @param string $field
1603
+ * @param array $input
1604
+ *
1605
+ * @return mixed
1606
+ */
1607
+ protected function validate_street_address($field, $input, $param = null) {
1608
+ if (!isset($input[$field]) || empty($input[$field])) {
1609
+ return;
1610
+ }
1611
+
1612
+ // Theory: 1 number, 1 or more spaces, 1 or more words
1613
+ $hasLetter = preg_match('/[a-zA-Z]/', $input[$field]);
1614
+ $hasDigit = preg_match('/\d/', $input[$field]);
1615
+ $hasSpace = preg_match('/\s/', $input[$field]);
1616
+
1617
+ $passes = $hasLetter && $hasDigit && $hasSpace;
1618
+
1619
+ if (!$passes) {
1620
+ return array(
1621
+ 'field' => $field,
1622
+ 'value' => $input[$field],
1623
+ 'rule' => __FUNCTION__,
1624
+ 'param' => $param,
1625
+ );
1626
+ }
1627
+ }
1628
+
1629
+ /**
1630
+ * Determine if the provided value is a valid IBAN.
1631
+ *
1632
+ * Usage: '<index>' => 'iban'
1633
+ *
1634
+ * @param string $field
1635
+ * @param array $input
1636
+ *
1637
+ * @return mixed
1638
+ */
1639
+ protected function validate_iban($field, $input, $param = null) {
1640
+ if (!isset($input[$field]) || empty($input[$field])) {
1641
+ return;
1642
+ }
1643
+
1644
+ static $character = array(
1645
+ 'A' => 10, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15, 'G' => 16,
1646
+ 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22,
1647
+ 'N' => 23, 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28,
1648
+ 'T' => 29, 'U' => 30, 'V' => 31, 'W' => 32, 'X' => 33, 'Y' => 34,
1649
+ 'Z' => 35, 'B' => 11
1650
+ );
1651
+
1652
+ if (!preg_match("/\A[A-Z]{2}\d{2} ?[A-Z\d]{4}( ?\d{4}){1,} ?\d{1,4}\z/", $input[$field])) {
1653
+ return array(
1654
+ 'field' => $field,
1655
+ 'value' => $input[$field],
1656
+ 'rule' => __FUNCTION__,
1657
+ 'param' => $param,
1658
+ );
1659
+ }
1660
+
1661
+ $iban = str_replace(' ', '', $input[$field]);
1662
+ $iban = substr($iban, 4) . substr($iban, 0, 4);
1663
+ $iban = strtr($iban, $character);
1664
+
1665
+ if (bcmod($iban, 97) != 1) {
1666
+ return array(
1667
+ 'field' => $field,
1668
+ 'value' => $input[$field],
1669
+ 'rule' => __FUNCTION__,
1670
+ 'param' => $param,
1671
+ );
1672
+ }
1673
+ }
1674
+
1675
+ /**
1676
+ * Determine if the provided input is a valid date (ISO 8601).
1677
+ *
1678
+ * Usage: '<index>' => 'date'
1679
+ *
1680
+ * @param string $field
1681
+ * @param string $input date ('Y-m-d') or datetime ('Y-m-d H:i:s')
1682
+ * @param null $param
1683
+ *
1684
+ * @return mixed
1685
+ */
1686
+ protected function validate_date($field, $input, $param = null) {
1687
+ if (!isset($input[$field]) || empty($input[$field])) {
1688
+ return;
1689
+ }
1690
+
1691
+ $cdate1 = date('Y-m-d', strtotime($input[$field]));
1692
+ $cdate2 = date('Y-m-d H:i:s', strtotime($input[$field]));
1693
+
1694
+ if ($cdate1 != $input[$field] && $cdate2 != $input[$field]) {
1695
+ return array(
1696
+ 'field' => $field,
1697
+ 'value' => $input[$field],
1698
+ 'rule' => __FUNCTION__,
1699
+ 'param' => $param,
1700
+ );
1701
+ }
1702
+ }
1703
+
1704
+ /**
1705
+ * Determine if the provided input meets age requirement (ISO 8601).
1706
+ *
1707
+ * Usage: '<index>' => 'min_age,13'
1708
+ *
1709
+ * @param string $field
1710
+ * @param string $input date ('Y-m-d') or datetime ('Y-m-d H:i:s')
1711
+ * @param string $param int
1712
+ *
1713
+ * @return mixed
1714
+ */
1715
+ protected function validate_min_age($field, $input, $param = null) {
1716
+ if (!isset($input[$field]) || empty($input[$field])) {
1717
+ return;
1718
+ }
1719
+
1720
+ $cdate1 = new DateTime(date('Y-m-d', strtotime($input[$field])));
1721
+ $today = new DateTime(date('d-m-Y'));
1722
+
1723
+ $interval = $cdate1->diff($today);
1724
+ $age = $interval->y;
1725
+
1726
+ if ($age <= $param) {
1727
+ return array(
1728
+ 'field' => $field,
1729
+ 'value' => $input[$field],
1730
+ 'rule' => __FUNCTION__,
1731
+ 'param' => $param,
1732
+ );
1733
+ }
1734
+ }
1735
+
1736
+ /**
1737
+ * Determine if the provided numeric value is lower or equal to a specific value.
1738
+ *
1739
+ * Usage: '<index>' => 'max_numeric,50'
1740
+ *
1741
+ * @param string $field
1742
+ * @param array $input
1743
+ * @param null $param
1744
+ *
1745
+ * @return mixed
1746
+ */
1747
+ protected function validate_max_numeric($field, $input, $param = null) {
1748
+ if (!isset($input[$field]) || empty($input[$field])) {
1749
+ return;
1750
+ }
1751
+
1752
+ if (is_numeric($input[$field]) && is_numeric($param) && ($input[$field] <= $param)) {
1753
+ return;
1754
+ }
1755
+
1756
+ return array(
1757
+ 'field' => $field,
1758
+ 'value' => $input[$field],
1759
+ 'rule' => __FUNCTION__,
1760
+ 'param' => $param,
1761
+ );
1762
+ }
1763
+
1764
+ /**
1765
+ * Determine if the provided numeric value is higher or equal to a specific value.
1766
+ *
1767
+ * Usage: '<index>' => 'min_numeric,1'
1768
+ *
1769
+ * @param string $field
1770
+ * @param array $input
1771
+ * @param null $param
1772
+ *
1773
+ * @return mixed
1774
+ */
1775
+ protected function validate_min_numeric($field, $input, $param = null) {
1776
+ if (!isset($input[$field])) {
1777
+ return;
1778
+ }
1779
+
1780
+ if (is_numeric($input[$field]) && is_numeric($param) && ($input[$field] >= $param)) {
1781
+ return;
1782
+ }
1783
+
1784
+ return array(
1785
+ 'field' => $field,
1786
+ 'value' => $input[$field],
1787
+ 'rule' => __FUNCTION__,
1788
+ 'param' => $param,
1789
+ );
1790
+ }
1791
+
1792
+ /**
1793
+ * Determine if the provided value starts with param.
1794
+ *
1795
+ * Usage: '<index>' => 'starts,Z'
1796
+ *
1797
+ * @param string $field
1798
+ * @param array $input
1799
+ *
1800
+ * @return mixed
1801
+ */
1802
+ protected function validate_starts($field, $input, $param = null) {
1803
+ if (!isset($input[$field]) || empty($input[$field])) {
1804
+ return;
1805
+ }
1806
+
1807
+ if (strpos($input[$field], $param) !== 0) {
1808
+ return array(
1809
+ 'field' => $field,
1810
+ 'value' => $input[$field],
1811
+ 'rule' => __FUNCTION__,
1812
+ 'param' => $param,
1813
+ );
1814
+ }
1815
+ }
1816
+
1817
+ /**
1818
+ * checks if a file was uploaded.
1819
+ *
1820
+ * Usage: '<index>' => 'required_file'
1821
+ *
1822
+ * @param string $field
1823
+ * @param array $input
1824
+ *
1825
+ * @return mixed
1826
+ */
1827
+ protected function validate_required_file($field, $input, $param = null) {
1828
+ if ($input[$field]['error'] !== 4) {
1829
+ return;
1830
+ }
1831
+
1832
+ return array(
1833
+ 'field' => $field,
1834
+ 'value' => $input[$field],
1835
+ 'rule' => __FUNCTION__,
1836
+ 'param' => $param,
1837
+ );
1838
+ }
1839
+
1840
+ /**
1841
+ * check the uploaded file for extension
1842
+ * for now checks onlt the ext should add mime type check.
1843
+ *
1844
+ * Usage: '<index>' => 'starts,Z'
1845
+ *
1846
+ * @param string $field
1847
+ * @param array $input
1848
+ *
1849
+ * @return mixed
1850
+ */
1851
+ protected function validate_extension($field, $input, $param = null) {
1852
+ if ($input[$field]['error'] !== 4) {
1853
+ $param = trim(strtolower($param));
1854
+ $allowed_extensions = explode(';', $param);
1855
+
1856
+ $path_info = pathinfo($input[$field]['name']);
1857
+ $extension = $path_info['extension'];
1858
+
1859
+ if (in_array($extension, $allowed_extensions)) {
1860
+ return;
1861
+ }
1862
+
1863
+ return array(
1864
+ 'field' => $field,
1865
+ 'value' => $input[$field],
1866
+ 'rule' => __FUNCTION__,
1867
+ 'param' => $param,
1868
+ );
1869
+ }
1870
+ }
1871
+
1872
+ /**
1873
+ * Determine if the provided field value equals current field value.
1874
+ *
1875
+ * Usage: '<index>' => 'equalsfield,Z'
1876
+ *
1877
+ * @param string $field
1878
+ * @param string $input
1879
+ * @param string $param field to compare with
1880
+ *
1881
+ * @return mixed
1882
+ */
1883
+ protected function validate_equalsfield($field, $input, $param = null) {
1884
+ if (!isset($input[$field]) || empty($input[$field])) {
1885
+ return;
1886
+ }
1887
+
1888
+ if ($input[$field] == $input[$param]) {
1889
+ return;
1890
+ }
1891
+
1892
+ return array(
1893
+ 'field' => $field,
1894
+ 'value' => $input[$field],
1895
+ 'rule' => __FUNCTION__,
1896
+ 'param' => $param,
1897
+ );
1898
+ }
1899
+
1900
+ /**
1901
+ * Determine if the provided field value is a valid GUID (v4)
1902
+ *
1903
+ * Usage: '<index>' => 'guidv4'
1904
+ *
1905
+ * @param string $field
1906
+ * @param string $input
1907
+ * @param string $param field to compare with
1908
+ *
1909
+ * @return mixed
1910
+ */
1911
+ protected function validate_guidv4($field, $input, $param = null) {
1912
+ if (!isset($input[$field]) || empty($input[$field])) {
1913
+ return;
1914
+ }
1915
+
1916
+ if (preg_match("/\{?[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}?$/", $input[$field])) {
1917
+ return;
1918
+ }
1919
+
1920
+ return array(
1921
+ 'field' => $field,
1922
+ 'value' => $input[$field],
1923
+ 'rule' => __FUNCTION__,
1924
+ 'param' => $param,
1925
+ );
1926
+ }
1927
+
1928
+ /**
1929
+ * Trims whitespace only when the value is a scalar.
1930
+ *
1931
+ * @param mixed $value
1932
+ *
1933
+ * @return mixed
1934
+ */
1935
+ private function trimScalar($value) {
1936
+ if (is_scalar($value)) {
1937
+ $value = trim($value);
1938
+ }
1939
+
1940
+ return $value;
1941
+ }
1942
+
1943
+ /**
1944
+ * Determine if the provided value is a valid phone number.
1945
+ *
1946
+ * Usage: '<index>' => 'phone_number'
1947
+ *
1948
+ * @param string $field
1949
+ * @param array $input
1950
+ *
1951
+ * @return mixed
1952
+ *
1953
+ * Examples:
1954
+ *
1955
+ * 555-555-5555: valid
1956
+ * 5555425555: valid
1957
+ * 555 555 5555: valid
1958
+ * 1(519) 555-4444: valid
1959
+ * 1 (519) 555-4422: valid
1960
+ * 1-555-555-5555: valid
1961
+ * 1-(555)-555-5555: valid
1962
+ */
1963
+ protected function validate_phone_number($field, $input, $param = null) {
1964
+ if (!isset($input[$field]) || empty($input[$field])) {
1965
+ return;
1966
+ }
1967
+
1968
+ $regex = '/^(\d[\s-]?)?[\(\[\s-]{0,2}?\d{3}[\)\]\s-]{0,2}?\d{3}[\s-]?\d{4}$/i';
1969
+ if (!preg_match($regex, $input[$field])) {
1970
+ return array(
1971
+ 'field' => $field,
1972
+ 'value' => $input[$field],
1973
+ 'rule' => __FUNCTION__,
1974
+ 'param' => $param,
1975
+ );
1976
+ }
1977
+ }
1978
+
1979
+ /**
1980
+ * Custom regex validator.
1981
+ *
1982
+ * Usage: '<index>' => 'regex&&&/regex/'
1983
+ *
1984
+ * @param string $field
1985
+ * @param array $input
1986
+ *
1987
+ * @return mixed
1988
+ */
1989
+ protected function validate_regex($field, $input, $param = null) {
1990
+ if (!isset($input[$field]) || empty($input[$field])) {
1991
+ return;
1992
+ }
1993
+
1994
+ $regex = $param;
1995
+ if (!preg_match($regex, $input[$field])) {
1996
+ return array(
1997
+ 'field' => $field,
1998
+ 'value' => $input[$field],
1999
+ 'rule' => __FUNCTION__,
2000
+ 'param' => $param,
2001
+ );
2002
+ }
2003
+ }
2004
+
2005
+ /**
2006
+ * Json validatior.
2007
+ *
2008
+ * Usage: '<index>' => 'valid_json_string'
2009
+ *
2010
+ * @param string $field
2011
+ * @param array $input
2012
+ *
2013
+ * @return mixed
2014
+ */
2015
+ protected function validate_valid_json_string($field, $input, $param = null) {
2016
+ if (!isset($input[$field]) || empty($input[$field])) {
2017
+ return;
2018
+ }
2019
+
2020
+ if (!is_string($input[$field]) || !is_object(json_decode($input[$field]))) {
2021
+ return array(
2022
+ 'field' => $field,
2023
+ 'value' => $input[$field],
2024
+ 'rule' => __FUNCTION__,
2025
+ 'param' => $param,
2026
+ );
2027
+ }
2028
+ }
2029
+ } // EOC
classes/libs/parsers.php ADDED
@@ -0,0 +1,789 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\libs;
3
+ /**
4
+ * WordPress eXtended RSS file parser implementations
5
+ *
6
+ * @package WordPress
7
+ * @subpackage Importer
8
+ */
9
+
10
+ /**
11
+ * WordPress Importer class for managing parsing of WXR files.
12
+ */
13
+ class WXR_Parser {
14
+ function parse($file) {
15
+
16
+ // Attempt to use proper XML parsers first
17
+ if (extension_loaded('simplexml')) {
18
+ $parser = new WXR_Parser_SimpleXML;
19
+ $result = $parser->parse($file);
20
+
21
+ // If SimpleXML succeeds or this is an invalid WXR file then return the results
22
+ if (!is_wp_error($result) || 'SimpleXML_parse_error' != $result->get_error_code())
23
+ return $result;
24
+ } else if (extension_loaded('xml')) {
25
+ $parser = new WXR_Parser_XML;
26
+ $result = $parser->parse($file);
27
+
28
+ // If XMLParser succeeds or this is an invalid WXR file then return the results
29
+ if (!is_wp_error($result) || 'XML_parse_error' != $result->get_error_code())
30
+ return $result;
31
+ }
32
+
33
+ // We have a malformed XML file, so display the error and fallthrough to regex
34
+ if (isset($result) && defined('IMPORT_DEBUG') && IMPORT_DEBUG) {
35
+ echo '<pre>';
36
+ if ('SimpleXML_parse_error' == $result->get_error_code()) {
37
+ foreach ($result->get_error_data() as $error)
38
+ echo $error->line . ':' . $error->column . ' ' . esc_html($error->message) . "\n";
39
+ } else if ('XML_parse_error' == $result->get_error_code()) {
40
+ $error = $result->get_error_data();
41
+ echo $error[0] . ':' . $error[1] . ' ' . esc_html($error[2]);
42
+ }
43
+ echo '</pre>';
44
+ echo '<p><strong>' . __('There was an error when reading this WXR file', 'wordpress-importer') . '</strong><br />';
45
+ echo __('Details are shown above. The importer will now try again with a different parser...', 'wordpress-importer') . '</p>';
46
+ }
47
+
48
+ // use regular expressions if nothing else available or this is bad XML
49
+ $parser = new WXR_Parser_Regex;
50
+ return $parser->parse($file);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * WXR Parser that makes use of the SimpleXML PHP extension.
56
+ */
57
+ class WXR_Parser_SimpleXML {
58
+ function parse($file) {
59
+ $authors = $posts = $categories = $tags = $time_slots = $terms = array();
60
+
61
+ $internal_errors = libxml_use_internal_errors(true);
62
+
63
+ $dom = new \DOMDocument;
64
+ $old_value = null;
65
+ if (function_exists('libxml_disable_entity_loader')) {
66
+ $old_value = libxml_disable_entity_loader(true);
67
+ }
68
+ $success = $dom->loadXML(file_get_contents($file));
69
+ if (!is_null($old_value)) {
70
+ libxml_disable_entity_loader($old_value);
71
+ }
72
+
73
+ if (!$success || isset($dom->doctype)) {
74
+ return new \WP_Error('SimpleXML_parse_error', __('There was an error when reading this WXR file', 'wordpress-importer'), libxml_get_errors());
75
+ }
76
+
77
+ $xml = simplexml_import_dom($dom);
78
+ unset($dom);
79
+
80
+ // halt if loading produces an error
81
+ if (!$xml)
82
+ return new \WP_Error('SimpleXML_parse_error', __('There was an error when reading this WXR file', 'wordpress-importer'), libxml_get_errors());
83
+
84
+ $wxr_version = $xml->xpath('/rss/channel/wp:wxr_version');
85
+ if (!$wxr_version)
86
+ return new \WP_Error('WXR_parse_error', __('This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer'));
87
+
88
+ $wxr_version = (string)trim($wxr_version[0]);
89
+ // confirm that we are dealing with the correct file format
90
+ if (!preg_match('/^\d+\.\d+$/', $wxr_version))
91
+ return new \WP_Error('WXR_parse_error', __('This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer'));
92
+
93
+ $base_url = $xml->xpath('/rss/channel/wp:base_site_url');
94
+ $base_url = (string)trim($base_url[0]);
95
+
96
+ $namespaces = $xml->getDocNamespaces();
97
+ if (!isset($namespaces['wp']))
98
+ $namespaces['wp'] = 'http://wordpress.org/export/1.1/';
99
+ if (!isset($namespaces['excerpt']))
100
+ $namespaces['excerpt'] = 'http://wordpress.org/export/1.1/excerpt/';
101
+
102
+ // grab authors
103
+ foreach ($xml->xpath('/rss/channel/wp:author') as $author_arr) {
104
+ $a = $author_arr->children($namespaces['wp']);
105
+ $login = (string)$a->author_login;
106
+ $authors[$login] = array(
107
+ 'author_id' => (int)$a->author_id,
108
+ 'author_login' => $login,
109
+ 'author_email' => (string)$a->author_email,
110
+ 'author_display_name' => (string)$a->author_display_name,
111
+ 'author_first_name' => (string)$a->author_first_name,
112
+ 'author_last_name' => (string)$a->author_last_name
113
+ );
114
+ }
115
+
116
+ // grab cats, tags and terms
117
+ foreach ($xml->xpath('/rss/channel/wp:category') as $term_arr) {
118
+ $t = $term_arr->children($namespaces['wp']);
119
+ $categories[] = array(
120
+ 'term_id' => (int)$t->term_id,
121
+ 'category_nicename' => (string)$t->category_nicename,
122
+ 'category_parent' => (string)$t->category_parent,
123
+ 'cat_name' => (string)$t->cat_name,
124
+ 'category_description' => (string)$t->category_description
125
+ );
126
+ }
127
+
128
+ foreach ($xml->xpath('/rss/channel/wp:tag') as $term_arr) {
129
+ $t = $term_arr->children($namespaces['wp']);
130
+ $tags[] = array(
131
+ 'term_id' => (int)$t->term_id,
132
+ 'tag_slug' => (string)$t->tag_slug,
133
+ 'tag_name' => (string)$t->tag_name,
134
+ 'tag_description' => (string)$t->tag_description
135
+ );
136
+ }
137
+
138
+ foreach ($xml->xpath('/rss/channel/wp:term') as $term_arr) {
139
+ $t = $term_arr->children($namespaces['wp']);
140
+ $terms[] = array(
141
+ 'term_id' => (int)$t->term_id,
142
+ 'term_taxonomy' => (string)$t->term_taxonomy,
143
+ 'slug' => (string)$t->term_slug,
144
+ 'term_parent' => (string)$t->term_parent,
145
+ 'term_name' => (string)$t->term_name,
146
+ 'term_description' => (string)$t->term_description
147
+ );
148
+ }
149
+
150
+ // grab posts
151
+ foreach ($xml->channel->item as $item) {
152
+ $post = array(
153
+ 'post_title' => (string)$item->title,
154
+ 'guid' => (string)$item->guid,
155
+ );
156
+
157
+ $dc = $item->children('http://purl.org/dc/elements/1.1/');
158
+ $post['post_author'] = (string)$dc->creator;
159
+
160
+ $content = $item->children('http://purl.org/rss/1.0/modules/content/');
161
+ $excerpt = $item->children($namespaces['excerpt']);
162
+ $post['post_content'] = (string)$content->encoded;
163
+ $post['post_excerpt'] = (string)$excerpt->encoded;
164
+
165
+ $wp = $item->children($namespaces['wp']);
166
+ $post['post_id'] = (int)$wp->post_id;
167
+ $post['post_date'] = (string)$wp->post_date;
168
+ $post['post_date_gmt'] = (string)$wp->post_date_gmt;
169
+ $post['comment_status'] = (string)$wp->comment_status;
170
+ $post['ping_status'] = (string)$wp->ping_status;
171
+ $post['post_name'] = (string)$wp->post_name;
172
+ $post['status'] = (string)$wp->status;
173
+ $post['post_parent'] = (int)$wp->post_parent;
174
+ $post['menu_order'] = (int)$wp->menu_order;
175
+ $post['post_type'] = (string)$wp->post_type;
176
+ $post['post_password'] = (string)$wp->post_password;
177
+ $post['is_sticky'] = (int)$wp->is_sticky;
178
+
179
+ if (isset($wp->attachment_url))
180
+ $post['attachment_url'] = (string)$wp->attachment_url;
181
+
182
+ foreach ($item->category as $c) {
183
+ $att = $c->attributes();
184
+ if (isset($att['nicename']))
185
+ $post['terms'][] = array(
186
+ 'name' => (string)$c,
187
+ 'slug' => (string)$att['nicename'],
188
+ 'domain' => (string)$att['domain']
189
+ );
190
+ }
191
+
192
+ foreach ($wp->postmeta as $meta) {
193
+ $post['postmeta'][] = array(
194
+ 'key' => (string)$meta->meta_key,
195
+ 'value' => (string)$meta->meta_value
196
+ );
197
+ }
198
+
199
+ foreach ($wp->timeslot as $time) {
200
+ $post['timeslot'][] = array(
201
+ 'column' => (string)$time->column,
202
+ 'event' => (string)$time->event,
203
+ 'event_start' => (string)$time->event_start,
204
+ 'event_end' => (string)$time->event_end,
205
+ 'user_id' => (string)$time->user_id,
206
+ 'description' => (string)$time->description,
207
+ );
208
+ }
209
+
210
+
211
+ foreach ($wp->comment as $comment) {
212
+ $meta = array();
213
+ if (isset($comment->commentmeta)) {
214
+ foreach ($comment->commentmeta as $m) {
215
+ $meta[] = array(
216
+ 'key' => (string)$m->meta_key,
217
+ 'value' => (string)$m->meta_value
218
+ );
219
+ }
220
+ }
221
+
222
+ $post['comments'][] = array(
223
+ 'comment_id' => (int)$comment->comment_id,
224
+ 'comment_author' => (string)$comment->comment_author,
225
+ 'comment_author_email' => (string)$comment->comment_author_email,
226
+ 'comment_author_IP' => (string)$comment->comment_author_IP,
227
+ 'comment_author_url' => (string)$comment->comment_author_url,
228
+ 'comment_date' => (string)$comment->comment_date,
229
+ 'comment_date_gmt' => (string)$comment->comment_date_gmt,
230
+ 'comment_content' => (string)$comment->comment_content,
231
+ 'comment_approved' => (string)$comment->comment_approved,
232
+ 'comment_type' => (string)$comment->comment_type,
233
+ 'comment_parent' => (string)$comment->comment_parent,
234
+ 'comment_user_id' => (int)$comment->comment_user_id,
235
+ 'commentmeta' => $meta,
236
+ );
237
+ }
238
+
239
+ $posts[] = $post;
240
+ }
241
+ // grab time_slots
242
+ foreach ($xml->channel->timeslot as $time) {
243
+ $time_slots[] = array(
244
+ 'column' => (int)$time->column,
245
+ 'event' => (int)$time->event,
246
+ 'event_start' => (string)$time->event_start,
247
+ 'event_end' => (string)$time->event_end,
248
+ 'user_id' => (int)$time->user_id,
249
+ 'description' => (string)$time->description,
250
+ );
251
+ }
252
+ return array(
253
+ 'authors' => $authors,
254
+ 'posts' => $posts,
255
+ 'categories' => $categories,
256
+ 'tags' => $tags,
257
+ 'terms' => $terms,
258
+ 'time_slots' => $time_slots,
259
+ 'base_url' => $base_url,
260
+ 'version' => $wxr_version
261
+ );
262
+ }
263
+ }
264
+
265
+ /**
266
+ * WXR Parser that makes use of the XML Parser PHP extension.
267
+ */
268
+ class WXR_Parser_XML {
269
+ var $wp_tags = array(
270
+ 'wp:post_id', 'wp:post_date', 'wp:post_date_gmt', 'wp:comment_status', 'wp:ping_status', 'wp:attachment_url',
271
+ 'wp:status', 'wp:post_name', 'wp:post_parent', 'wp:menu_order', 'wp:post_type', 'wp:post_password',
272
+ 'wp:is_sticky', 'wp:term_id', 'wp:category_nicename', 'wp:category_parent', 'wp:cat_name', 'wp:category_description',
273
+ 'wp:tag_slug', 'wp:tag_name', 'wp:tag_description', 'wp:term_taxonomy', 'wp:term_parent',
274
+ 'wp:term_name', 'wp:term_description', 'wp:author_id', 'wp:author_login', 'wp:author_email', 'wp:author_display_name',
275
+ 'wp:author_first_name', 'wp:author_last_name',
276
+ );
277
+ var $wp_sub_tags = array(
278
+ 'wp:comment_id', 'wp:comment_author', 'wp:comment_author_email', 'wp:comment_author_url',
279
+ 'wp:comment_author_IP', 'wp:comment_date', 'wp:comment_date_gmt', 'wp:comment_content',
280
+ 'wp:comment_approved', 'wp:comment_type', 'wp:comment_parent', 'wp:comment_user_id',
281
+ );
282
+
283
+ function parse($file) {
284
+ $this->wxr_version = $this->in_post = $this->cdata = $this->data = $this->sub_data = $this->in_tag = $this->in_sub_tag = false;
285
+ $this->authors = $this->posts = $this->term = $this->category = $this->tag = $this->time_slots = array();
286
+
287
+ $xml = xml_parser_create('UTF-8');
288
+ xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1);
289
+ xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0);
290
+ xml_set_object($xml, $this);
291
+ xml_set_character_data_handler($xml, 'cdata');
292
+ xml_set_element_handler($xml, 'tag_open', 'tag_close');
293
+
294
+ if (!xml_parse($xml, file_get_contents($file), true)) {
295
+ $current_line = xml_get_current_line_number($xml);
296
+ $current_column = xml_get_current_column_number($xml);
297
+ $error_code = xml_get_error_code($xml);
298
+ $error_string = xml_error_string($error_code);
299
+ return new \WP_Error('XML_parse_error', 'There was an error when reading this WXR file', array($current_line, $current_column, $error_string));
300
+ }
301
+ xml_parser_free($xml);
302
+
303
+ if (!preg_match('/^\d+\.\d+$/', $this->wxr_version))
304
+ return new \WP_Error('WXR_parse_error', __('This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer'));
305
+
306
+ return array(
307
+ 'authors' => $this->authors,
308
+ 'posts' => $this->posts,
309
+ 'categories' => $this->category,
310
+ 'tags' => $this->tag,
311
+ 'terms' => $this->term,
312
+ 'base_url' => $this->base_url,
313
+ 'version' => $this->wxr_version,
314
+ 'time_slots' => $this->time_slots
315
+ );
316
+ }
317
+
318
+ function tag_open($parse, $tag, $attr) {
319
+ if (in_array($tag, $this->wp_tags)) {
320
+ $this->in_tag = substr($tag, 3);
321
+ return;
322
+ }
323
+
324
+ if (in_array($tag, $this->wp_sub_tags)) {
325
+ $this->in_sub_tag = substr($tag, 3);
326
+ return;
327
+ }
328
+
329
+ switch ($tag) {
330
+ case 'category':
331
+ if (isset($attr['domain'], $attr['nicename'])) {
332
+ $this->sub_data['domain'] = $attr['domain'];
333
+ $this->sub_data['slug'] = $attr['nicename'];
334
+ }
335
+ break;
336
+ case 'item':
337
+ $this->in_post = true;
338
+ case 'title':
339
+ if ($this->in_post) $this->in_tag = 'post_title';
340
+ break;
341
+ case 'timeslot':
342
+ break;
343
+ case 'column':
344
+ $this->in_tag = 'column';
345
+ break;
346
+ case 'event':
347
+ $this->in_tag = 'event';
348
+ break;
349
+ case 'event_start':
350
+ $this->in_tag = 'event_start';
351
+ break;
352
+ case 'event_end':
353
+ $this->in_tag = 'event_end';
354
+ break;
355
+ case 'user_id':
356
+ $this->in_tag = 'user_id';
357
+ break;
358
+ case 'description':
359
+ $this->in_tag = 'description';
360
+ break;
361
+ case 'guid':
362
+ $this->in_tag = 'guid';
363
+ break;
364
+ case 'dc:creator':
365
+ $this->in_tag = 'post_author';
366
+ break;
367
+ case 'content:encoded':
368
+ $this->in_tag = 'post_content';
369
+ break;
370
+ case 'excerpt:encoded':
371
+ $this->in_tag = 'post_excerpt';
372
+ break;
373
+ case 'wp:term_slug':
374
+ $this->in_tag = 'slug';
375
+ break;
376
+ case 'wp:meta_key':
377
+ $this->in_sub_tag = 'key';
378
+ break;
379
+ case 'wp:meta_value':
380
+ $this->in_sub_tag = 'value';
381
+ break;
382
+ }
383
+ }
384
+
385
+ function cdata($parser, $cdata) {
386
+ if (!trim($cdata))
387
+ return;
388
+
389
+ $this->cdata .= trim($cdata);
390
+ }
391
+
392
+ function tag_close($parser, $tag) {
393
+ switch ($tag) {
394
+ case 'wp:comment':
395
+ unset($this->sub_data['key'], $this->sub_data['value']); // remove meta sub_data
396
+ if (!empty($this->sub_data))
397
+ $this->data['comments'][] = $this->sub_data;
398
+ $this->sub_data = false;
399
+ break;
400
+ case 'wp:commentmeta':
401
+ $this->sub_data['commentmeta'][] = array(
402
+ 'key' => $this->sub_data['key'],
403
+ 'value' => $this->sub_data['value']
404
+ );
405
+ break;
406
+ case 'category':
407
+ if (!empty($this->sub_data)) {
408
+ $this->sub_data['name'] = $this->cdata;
409
+ $this->data['terms'][] = $this->sub_data;
410
+ }
411
+ $this->sub_data = false;
412
+ break;
413
+ case 'wp:postmeta':
414
+ if (!empty($this->sub_data))
415
+ $this->data['postmeta'][] = $this->sub_data;
416
+ $this->sub_data = false;
417
+ break;
418
+ case 'timeslot':
419
+ $this->time_slots[] = $this->data;
420
+ $this->data = false;
421
+ break;
422
+ case 'item':
423
+ $this->posts[] = $this->data;
424
+ $this->data = false;
425
+ break;
426
+ case 'wp:category':
427
+ case 'wp:tag':
428
+ case 'wp:term':
429
+ $n = substr($tag, 3);
430
+ array_push($this->$n, $this->data);
431
+ $this->data = false;
432
+ break;
433
+ case 'wp:author':
434
+ if (!empty($this->data['author_login']))
435
+ $this->authors[$this->data['author_login']] = $this->data;
436
+ $this->data = false;
437
+ break;
438
+ case 'wp:base_site_url':
439
+ $this->base_url = $this->cdata;
440
+ break;
441
+ case 'wp:wxr_version':
442
+ $this->wxr_version = $this->cdata;
443
+ break;
444
+
445
+ default:
446
+ if ($this->in_sub_tag) {
447
+ $this->sub_data[$this->in_sub_tag] = !empty($this->cdata) ? $this->cdata : '';
448
+ $this->in_sub_tag = false;
449
+ } else if ($this->in_tag) {
450
+ $this->data[$this->in_tag] = !empty($this->cdata) ? $this->cdata : '';
451
+ $this->in_tag = false;
452
+ }
453
+ }
454
+
455
+ $this->cdata = false;
456
+ }
457
+ }
458
+
459
+ /**
460
+ * WXR Parser that uses regular expressions. Fallback for installs without an XML parser.
461
+ */
462
+ class WXR_Parser_Regex {
463
+ var $authors = array();
464
+ var $posts = array();
465
+ var $categories = array();
466
+ var $tags = array();
467
+ var $terms = array();
468
+ var $time_slots = array();
469
+ var $base_url = '';
470
+
471
+ function __construct() {
472
+ $this->has_gzip = is_callable('gzopen');
473
+ }
474
+
475
+ function parse($file) {
476
+ $wxr_version = $in_post = $in_tag = false;
477
+ $post = $tag = '';
478
+ $fp = $this->fopen($file, 'r');
479
+ if ($fp) {
480
+ while (!$this->feof($fp)) {
481
+ $importline = rtrim($this->fgets($fp));
482
+
483
+ if (!$wxr_version && preg_match('|<wp:wxr_version>(\d+\.\d+)</wp:wxr_version>|', $importline, $version))
484
+ $wxr_version = $version[1];
485
+
486
+ if (false !== strpos($importline, '<wp:base_site_url>')) {
487
+ preg_match('|<wp:base_site_url>(.*?)</wp:base_site_url>|is', $importline, $url);
488
+ $this->base_url = $url[1];
489
+ continue;
490
+ }
491
+ if (false !== strpos($importline, '<wp:category>')) {
492
+ preg_match('|<wp:category>(.*?)</wp:category>|is', $importline, $category);
493
+ $this->categories[] = $this->process_category($category[1]);
494
+ continue;
495
+ }
496
+ if (false !== strpos($importline, '<wp:tag>')) {
497
+ preg_match('|<wp:tag>(.*?)</wp:tag>|is', $importline, $tag);
498
+ $this->tags[] = $this->process_tag($tag[1]);
499
+ continue;
500
+ }
501
+ if (false !== strpos($importline, '<wp:term>')) {
502
+ preg_match('|<wp:term>(.*?)</wp:term>|is', $importline, $term);
503
+ if (!empty($term)) {
504
+ $this->terms[] = $this->process_term($term[1]);
505
+ } else {
506
+ $in_tag = true;
507
+ }
508
+ continue;
509
+ }
510
+
511
+ if (false !== strpos($importline, '</wp:term>')) {
512
+ $tag .= $importline . "\n";
513
+ $this->terms[] = $this->process_term($tag);
514
+ $tag = '';
515
+ $in_tag = false;
516
+ continue;
517
+ }
518
+
519
+ if (false !== strpos($importline, '<timeslot>')) {
520
+ preg_match('|<timeslot>(.*?)</timeslot>|is', $importline, $time_slot);
521
+ if (!empty($time_slot)) {
522
+ $this->time_slots[] = $this->process_time_slot($time_slot[1]);
523
+ } else {
524
+ $in_tag = true;
525
+ }
526
+ continue;
527
+ }
528
+ if (false !== strpos($importline, '</timeslot>')) {
529
+ $tag .= $importline . "\n";
530
+ $this->time_slots[] = $this->process_time_slot($tag);
531
+ $tag = '';
532
+ $in_tag = false;
533
+ continue;
534
+ }
535
+
536
+
537
+ if (false !== strpos($importline, '<wp:author>')) {
538
+ preg_match('|<wp:author>(.*?)</wp:author>|is', $importline, $author);
539
+ $a = $this->process_author($author[1]);
540
+ $this->authors[$a['author_login']] = $a;
541
+ continue;
542
+ }
543
+ if (false !== strpos($importline, '<item>')) {
544
+ $in_post = true;
545
+ continue;
546
+ }
547
+ if (false !== strpos($importline, '</item>')) {
548
+ $in_post = false;
549
+ $this->posts[] = $this->process_post($post);
550
+ continue;
551
+ }
552
+ if ($in_post) {
553
+ $post .= $importline . "\n";
554
+ }
555
+
556
+ if ($in_tag) {
557
+ $tag .= $importline . "\n";
558
+ }
559
+ }
560
+
561
+ $this->fclose($fp);
562
+ }
563
+
564
+ if (!$wxr_version)
565
+ return new \WP_Error('WXR_parse_error', __('This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer'));
566
+
567
+ return array(
568
+ 'authors' => $this->authors,
569
+ 'posts' => $this->posts,
570
+ 'categories' => $this->categories,
571
+ 'tags' => $this->tags,
572
+ 'terms' => $this->terms,
573
+ 'base_url' => $this->base_url,
574
+ 'version' => $wxr_version,
575
+ 'time_slots' => $this->time_slots
576
+ );
577
+ }
578
+
579
+ function get_tag($string, $tag) {
580
+ preg_match("|<$tag.*?>(.*?)</$tag>|is", $string, $return);
581
+ if (isset($return[1])) {
582
+ if (substr($return[1], 0, 9) == '<![CDATA[') {
583
+ if (strpos($return[1], ']]]]><![CDATA[>') !== false) {
584
+ preg_match_all('|<!\[CDATA\[(.*?)\]\]>|s', $return[1], $matches);
585
+ $return = '';
586
+ foreach ($matches[1] as $match)
587
+ $return .= $match;
588
+ } else {
589
+ $return = preg_replace('|^<!\[CDATA\[(.*)\]\]>$|s', '$1', $return[1]);
590
+ }
591
+ } else {
592
+ $return = $return[1];
593
+ }
594
+ } else {
595
+ $return = '';
596
+ }
597
+ return $return;
598
+ }
599
+
600
+ function process_category($c) {
601
+ return array(
602
+ 'term_id' => $this->get_tag($c, 'wp:term_id'),
603
+ 'cat_name' => $this->get_tag($c, 'wp:cat_name'),
604
+ 'category_nicename' => $this->get_tag($c, 'wp:category_nicename'),
605
+ 'category_parent' => $this->get_tag($c, 'wp:category_parent'),
606
+ 'category_description' => $this->get_tag($c, 'wp:category_description'),
607
+ );
608
+ }
609
+
610
+ function process_tag($t) {
611
+ return array(
612
+ 'term_id' => $this->get_tag($t, 'wp:term_id'),
613
+ 'tag_name' => $this->get_tag($t, 'wp:tag_name'),
614
+ 'tag_slug' => $this->get_tag($t, 'wp:tag_slug'),
615
+ 'tag_description' => $this->get_tag($t, 'wp:tag_description'),
616
+ );
617
+ }
618
+
619
+ function process_term($t) {
620
+ return array(
621
+ 'term_id' => $this->get_tag($t, 'wp:term_id'),
622
+ 'term_taxonomy' => $this->get_tag($t, 'wp:term_taxonomy'),
623
+ 'slug' => $this->get_tag($t, 'wp:term_slug'),
624
+ 'term_parent' => $this->get_tag($t, 'wp:term_parent'),
625
+ 'term_name' => $this->get_tag($t, 'wp:term_name'),
626
+ 'term_description' => $this->get_tag($t, 'wp:term_description'),
627
+ );
628
+ }
629
+
630
+ function process_time_slot($t) {
631
+ return array(
632
+ 'column' => $this->get_tag($t, 'column'),
633
+ 'event' => $this->get_tag($t, 'event'),
634
+ 'event_start' => $this->get_tag($t, 'event_start'),
635
+ 'event_end' => $this->get_tag($t, 'event_end'),
636
+ 'user_id' => $this->get_tag($t, 'user_id'),
637
+ 'description' => $this->get_tag($t, 'description'),
638
+ );
639
+ }
640
+
641
+ function process_author($a) {
642
+ return array(
643
+ 'author_id' => $this->get_tag($a, 'wp:author_id'),
644
+ 'author_login' => $this->get_tag($a, 'wp:author_login'),
645
+ 'author_email' => $this->get_tag($a, 'wp:author_email'),
646
+ 'author_display_name' => $this->get_tag($a, 'wp:author_display_name'),
647
+ 'author_first_name' => $this->get_tag($a, 'wp:author_first_name'),
648
+ 'author_last_name' => $this->get_tag($a, 'wp:author_last_name'),
649
+ );
650
+ }
651
+
652
+ function process_post($post) {
653
+ $post_id = $this->get_tag($post, 'wp:post_id');
654
+ $post_title = $this->get_tag($post, 'title');
655
+ $post_date = $this->get_tag($post, 'wp:post_date');
656
+ $post_date_gmt = $this->get_tag($post, 'wp:post_date_gmt');
657
+ $comment_status = $this->get_tag($post, 'wp:comment_status');
658
+ $ping_status = $this->get_tag($post, 'wp:ping_status');
659
+ $status = $this->get_tag($post, 'wp:status');
660
+ $post_name = $this->get_tag($post, 'wp:post_name');
661
+ $post_parent = $this->get_tag($post, 'wp:post_parent');
662
+ $menu_order = $this->get_tag($post, 'wp:menu_order');
663
+ $post_type = $this->get_tag($post, 'wp:post_type');
664
+ $post_password = $this->get_tag($post, 'wp:post_password');
665
+ $is_sticky = $this->get_tag($post, 'wp:is_sticky');
666
+ $guid = $this->get_tag($post, 'guid');
667
+ $post_author = $this->get_tag($post, 'dc:creator');
668
+
669
+ $post_excerpt = $this->get_tag($post, 'excerpt:encoded');
670
+ $post_excerpt = preg_replace_callback('|<(/?[A-Z]+)|', array(&$this, '_normalize_tag'), $post_excerpt);
671
+ $post_excerpt = str_replace('<br>', '<br />', $post_excerpt);
672
+ $post_excerpt = str_replace('<hr>', '<hr />', $post_excerpt);
673
+
674
+ $post_content = $this->get_tag($post, 'content:encoded');
675
+ $post_content = preg_replace_callback('|<(/?[A-Z]+)|', array(&$this, '_normalize_tag'), $post_content);
676
+ $post_content = str_replace('<br>', '<br />', $post_content);
677
+ $post_content = str_replace('<hr>', '<hr />', $post_content);
678
+
679
+ $postdata = compact('post_id', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_excerpt',
680
+ 'post_title', 'status', 'post_name', 'comment_status', 'ping_status', 'guid', 'post_parent',
681
+ 'menu_order', 'post_type', 'post_password', 'is_sticky'
682
+ );
683
+
684
+ $attachment_url = $this->get_tag($post, 'wp:attachment_url');
685
+ if ($attachment_url)
686
+ $postdata['attachment_url'] = $attachment_url;
687
+
688
+ preg_match_all('|<category domain="([^"]+?)" nicename="([^"]+?)">(.+?)</category>|is', $post, $terms, PREG_SET_ORDER);
689
+ foreach ($terms as $t) {
690
+ $post_terms[] = array(
691
+ 'slug' => $t[2],
692
+ 'domain' => $t[1],
693
+ 'name' => str_replace(array('<![CDATA[', ']]>'), '', $t[3]),
694
+ );
695
+ }
696
+ if (!empty($post_terms)) $postdata['terms'] = $post_terms;
697
+
698
+ preg_match_all('|<wp:comment>(.+?)</wp:comment>|is', $post, $comments);
699
+ $comments = $comments[1];
700
+ if ($comments) {
701
+ foreach ($comments as $comment) {
702
+ preg_match_all('|<wp:commentmeta>(.+?)</wp:commentmeta>|is', $comment, $commentmeta);
703
+ $commentmeta = $commentmeta[1];
704
+ $c_meta = array();
705
+ foreach ($commentmeta as $m) {
706
+ $c_meta[] = array(
707
+ 'key' => $this->get_tag($m, 'wp:meta_key'),
708
+ 'value' => $this->get_tag($m, 'wp:meta_value'),
709
+ );
710
+ }
711
+
712
+ $post_comments[] = array(
713
+ 'comment_id' => $this->get_tag($comment, 'wp:comment_id'),
714
+ 'comment_author' => $this->get_tag($comment, 'wp:comment_author'),
715
+ 'comment_author_email' => $this->get_tag($comment, 'wp:comment_author_email'),
716
+ 'comment_author_IP' => $this->get_tag($comment, 'wp:comment_author_IP'),
717
+ 'comment_author_url' => $this->get_tag($comment, 'wp:comment_author_url'),
718
+ 'comment_date' => $this->get_tag($comment, 'wp:comment_date'),
719
+ 'comment_date_gmt' => $this->get_tag($comment, 'wp:comment_date_gmt'),
720
+ 'comment_content' => $this->get_tag($comment, 'wp:comment_content'),
721
+ 'comment_approved' => $this->get_tag($comment, 'wp:comment_approved'),
722
+ 'comment_type' => $this->get_tag($comment, 'wp:comment_type'),
723
+ 'comment_parent' => $this->get_tag($comment, 'wp:comment_parent'),
724
+ 'comment_user_id' => $this->get_tag($comment, 'wp:comment_user_id'),
725
+ 'commentmeta' => $c_meta,
726
+ );
727
+ }
728
+ }
729
+ if (!empty($post_comments)) $postdata['comments'] = $post_comments;
730
+
731
+ preg_match_all('|<wp:postmeta>(.+?)</wp:postmeta>|is', $post, $postmeta);
732
+ $postmeta = $postmeta[1];
733
+ if ($postmeta) {
734
+ foreach ($postmeta as $p) {
735
+ $post_postmeta[] = array(
736
+ 'key' => $this->get_tag($p, 'wp:meta_key'),
737
+ 'value' => $this->get_tag($p, 'wp:meta_value'),
738
+ );
739
+ }
740
+ }
741
+ if (!empty($post_postmeta)) $postdata['postmeta'] = $post_postmeta;
742
+
743
+ preg_match_all('|<wp:timeslot>(.+?)</wp:timeslot>|is', $post, $timeslot);
744
+ $timeslot = $timeslot[1];
745
+ if ($timeslot) {
746
+ foreach ($timeslot as $p) {
747
+ $post_ptimeslot[] = array(
748
+ 'column' => $this->get_tag($p, 'wp:column'),
749
+ 'event' => $this->get_tag($p, 'wp:event'),
750
+ 'event_start' => $this->get_tag($p, 'wp:event_start'),
751
+ 'event_end' => $this->get_tag($p, 'wp:event_end'),
752
+ 'user_id' => $this->get_tag($p, 'wp:user_id'),
753
+ 'description' => $this->get_tag($p, 'wp:description')
754
+ );
755
+ }
756
+ }
757
+ if (!empty($post_timeslot)) $postdata['timeslot'] = $post_timeslot;
758
+
759
+ return $postdata;
760
+ }
761
+
762
+ function _normalize_tag($matches) {
763
+ return '<' . strtolower($matches[1]);
764
+ }
765
+
766
+ function fopen($filename, $mode = 'r') {
767
+ if ($this->has_gzip)
768
+ return gzopen($filename, $mode);
769
+ return fopen($filename, $mode);
770
+ }
771
+
772
+ function feof($fp) {
773
+ if ($this->has_gzip)
774
+ return gzeof($fp);
775
+ return feof($fp);
776
+ }
777
+
778
+ function fgets($fp, $len = 8192) {
779
+ if ($this->has_gzip)
780
+ return gzgets($fp, $len);
781
+ return fgets($fp, $len);
782
+ }
783
+
784
+ function fclose($fp) {
785
+ if ($this->has_gzip)
786
+ return gzclose($fp);
787
+ return fclose($fp);
788
+ }
789
+ }
classes/models/class-column.php ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\models;
4
+
5
+ use mp_timetable\plugin_core\classes\Model as Model;
6
+
7
+ /**
8
+ * Model Events
9
+ */
10
+ class Column extends Model {
11
+
12
+ protected static $instance;
13
+ protected $wpdb;
14
+
15
+ /**
16
+ * Column constructor.
17
+ */
18
+ public function __construct() {
19
+ parent::__construct();
20
+ global $wpdb;
21
+ $this->wpdb = $wpdb;
22
+ }
23
+
24
+ /**
25
+ * @return Column
26
+ */
27
+ public static function get_instance() {
28
+ if (null === self::$instance) {
29
+ self::$instance = new self();
30
+ }
31
+ return self::$instance;
32
+ }
33
+
34
+ /**
35
+ * Add column
36
+ *
37
+ * @param $columns
38
+ *
39
+ * @return array
40
+ */
41
+ public function set_column_columns($columns) {
42
+ $columns = array_slice($columns, 0, 2, true) + array("mp-column_timeslots_number" => __('Timeslots', 'mp-timetable')) + array_slice($columns, 2, count($columns) - 1, true);
43
+
44
+ return $columns;
45
+ }
46
+
47
+ /**
48
+ * Create event category admin link
49
+ *
50
+ * @param $column
51
+ */
52
+ public function get_column_columns($column) {
53
+ global $post;
54
+ switch ($column) {
55
+ case 'mp-column_timeslots_number':
56
+ $metaData = $this->count_events($post);
57
+ echo empty($metaData) ? "—" : $metaData;
58
+ break;
59
+ }
60
+
61
+ }
62
+
63
+ /**
64
+ * Count events
65
+ *
66
+ * @param $post
67
+ *
68
+ * @return int
69
+ */
70
+ public function count_events($post) {
71
+ $table_name = $this->get('events')->table_name;
72
+ $count = $this->wpdb->get_var("SELECT COUNT(*) FROM {$table_name} WHERE `column_id` = {$post->ID}");
73
+
74
+ return intval($count);
75
+ }
76
+
77
+ /**
78
+ * Client area order
79
+ *
80
+ * @param object $query
81
+ *
82
+ * @return mixed
83
+ */
84
+ public function clientarea_default_order($query) {
85
+ if (is_admin() || $query->is_main_query()) {
86
+ if ($query->get('post_type') === 'mp-column') {
87
+ $query->set('orderby', 'menu_order');
88
+ $query->set('order', 'ASC');
89
+ }
90
+ }
91
+
92
+ return $query;
93
+ }
94
+
95
+ /**
96
+ * @param array $params
97
+ *
98
+ * @return array
99
+ */
100
+ public function get_all_column($params = array()) {
101
+ $args = array(
102
+ 'post_type' => 'mp-column',
103
+ 'post_status' => 'publish',
104
+ 'order' => 'ASC',
105
+ 'orderby' => 'menu_order date',
106
+ 'posts_per_page' => -1,
107
+ 'post__in' => ''
108
+ );
109
+
110
+ $args = array_merge($args, $params);
111
+ return get_posts($args);
112
+ }
113
+
114
+ /**
115
+ * Columns by event category
116
+ *
117
+ * @param $terms
118
+ *
119
+ * @return array
120
+ */
121
+ public function get_columns_by_event_category($terms) {
122
+ $columns = array();
123
+ if (!is_array($terms)) {
124
+ $terms = explode(',', $terms);
125
+ }
126
+ $posts_array = get_posts(
127
+ array(
128
+ 'fields' => 'ids',
129
+ 'posts_per_page' => -1,
130
+ 'post_type' => 'mp-event',
131
+ 'post_status' => 'publish',
132
+ 'tax_query' => array(
133
+ array(
134
+ 'taxonomy' => 'mp-event_category',
135
+ 'field' => 'term_id',
136
+ 'terms' => $terms,
137
+ )
138
+ )
139
+ )
140
+ );
141
+ $ids = implode(',', $posts_array);
142
+ $event_data = $this->get('events')->get_events_data(array('column' => 'event_id', 'list' => $ids));
143
+ if (!empty($event_data)) {
144
+ foreach ($event_data as $event) {
145
+ if (in_array($event->event_id, $posts_array)) {
146
+ $columns[] = $event->column_id;
147
+ }
148
+ }
149
+ }
150
+ return array_unique($columns);
151
+ }
152
+
153
+ /**
154
+ * Render column metabox
155
+ *
156
+ * @param $post
157
+ */
158
+ public function render_column_options($post) {
159
+ $this->get_view()->render_html("column/metabox-column-options", array('post' => $post), true);
160
+ }
161
+
162
+ /**
163
+ * Render meta data
164
+ */
165
+ public function render_column_metas() {
166
+ $this->append_events();
167
+ }
168
+
169
+ public function append_events() {
170
+ global $post;
171
+ $data = $this->get('events')->get_event_data(array('field' => 'column_id', 'id' => $post->ID));
172
+ $events = (!empty($data)) ? $data : array();
173
+
174
+ $this->get_view()->get_template("theme/column-events", array('events' => $events));
175
+ }
176
+
177
+ /**
178
+ * Save meta data Column post type
179
+ *
180
+ * @param array $params
181
+ */
182
+ public function save_column_data(array $params) {
183
+ if (!empty($params['data'])) {
184
+ foreach ($params['data'] as $meta_key => $meta) {
185
+ if (!empty($meta)) {
186
+ update_post_meta($params['post']->ID, $meta_key, $meta);
187
+ } else {
188
+ delete_post_meta($params['post']->ID, $meta_key, $meta);
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Delete time-slots of the Column
196
+ *
197
+ * @param $post_id
198
+ *
199
+ * @return false|int
200
+ */
201
+ public function before_delete_column($post_id) {
202
+ $table_name = $this->get('events')->table_name;
203
+
204
+ return $this->wpdb->delete($table_name, array('column_id' => $post_id), array('%d'));
205
+ }
206
+ }
classes/models/class-events.php ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\models;
4
+
5
+ use mp_timetable\plugin_core\classes\Model as Model;
6
+ use mp_timetable\plugin_core\classes\modules\Taxonomy;
7
+
8
+
9
+ /**
10
+ * Model Events
11
+ */
12
+ class Events extends Model {
13
+
14
+ protected static $instance;
15
+ protected $wpdb;
16
+ protected $table_name;
17
+ protected $post_type;
18
+
19
+ /**
20
+ * Events constructor.
21
+ */
22
+ function __construct() {
23
+ parent::__construct();
24
+ global $wpdb;
25
+ $this->wpdb = $wpdb;
26
+ $this->post_type = 'mp-event';
27
+ $this->taxonomy_names = array(
28
+ 'tag' => 'mp-event_tag',
29
+ 'cat' => 'mp-event_category',
30
+ );
31
+ $this->table_name = $wpdb->prefix . "mp_timetable_data";
32
+ }
33
+
34
+ /**
35
+ * @return Events
36
+ */
37
+ public static function get_instance() {
38
+ if ( null === self::$instance ) {
39
+ self::$instance = new self();
40
+ }
41
+
42
+ return self::$instance;
43
+ }
44
+
45
+ /**
46
+ * Getter
47
+ *
48
+ * @param $property
49
+ *
50
+ * @return mixed
51
+ */
52
+ function __get( $property ) {
53
+ return $this->{$property};
54
+ }
55
+
56
+ /**
57
+ * Setter
58
+ *
59
+ * @param $property
60
+ * @param $value
61
+ *
62
+ * @return mixed
63
+ */
64
+ function __set( $property, $value ) {
65
+ return $this->{$property} = $value;
66
+ }
67
+
68
+ /**
69
+ * Render event metabox
70
+ *
71
+ * @param $post
72
+ * @param $metabox
73
+ */
74
+ public function render_event_data( $post, $metabox ) {
75
+ $time_format = get_option( 'time_format' );
76
+ $data[ 'columns' ] = $this->get( 'column' )->get_all_column();
77
+ if ( $time_format === 'H:i' ) {
78
+ $time_format_array = array( 'hours' => '0,23', 'am_pm' => false );
79
+ } elseif ( $time_format === 'g:i A' ) {
80
+ $time_format_array = array( 'hours' => '1,12', 'am_pm' => true );
81
+ } else {
82
+ $time_format_array = array( 'hours' => '0,23', 'am_pm' => false );
83
+ }
84
+ $event_data = $this->get_event_data( array( 'field' => 'event_id', 'id' => $post->ID ), 'event_start', false );
85
+
86
+ $this->get_view()->render_html( "events/metabox-event-data", array( 'event_data' => $event_data, 'args' => $metabox[ 'args' ], 'columns' => $data[ 'columns' ], 'date' => array( 'time_format' => $time_format_array ) ), true );
87
+ }
88
+
89
+ /**
90
+ *
91
+ * Get single event data by id
92
+ *
93
+ * @param $params
94
+ * @param string $order_by
95
+ * @param bool $publish
96
+ *
97
+ * @return array|null|object
98
+ */
99
+ public function get_event_data( $params, $order_by = 'event_start', $publish = true ) {
100
+ $publish_query_part = $publish ? " AND `post_status` = 'publish'" : '';
101
+ $table_posts = $this->wpdb->prefix . 'posts';
102
+
103
+ $event_data = $this->wpdb->get_results(
104
+ "SELECT t.*"
105
+ . " FROM $this->table_name t INNER JOIN"
106
+ . " ("
107
+ . " SELECT * FROM {$table_posts}"
108
+ . " WHERE `post_type` = 'mp-column' AND `post_status` = 'publish'"
109
+ . " ) p ON t.`column_id` = p.`ID`"
110
+ . " INNER JOIN ("
111
+ . " SELECT * FROM {$table_posts}"
112
+ . " WHERE `post_type` = '{$this->post_type}'{$publish_query_part}"
113
+ . " ) e ON t.`event_id` = e.`ID`"
114
+ . " WHERE t.`{$params["field"]}` = {$params['id']} "
115
+ . " ORDER BY p.`menu_order`, t.`{$order_by}`"
116
+ );
117
+
118
+ foreach ( $event_data as $key => $event ) {
119
+ $event_data[ $key ]->event_start = date( 'H:i', strtotime( $event_data[ $key ]->event_start ) );
120
+ $event_data[ $key ]->event_end = date( 'H:i', strtotime( $event_data[ $key ]->event_end ) );
121
+ $event_data[ $key ]->user = get_user_by( 'id', $event_data[ $key ]->user_id );
122
+ $event_data[ $key ]->post = get_post( $event_data[ $key ]->event_id );
123
+ }
124
+
125
+ return $event_data;
126
+ }
127
+
128
+ /**
129
+ * Render meta data
130
+ */
131
+ public function render_event_metas() {
132
+ $this->append_time_slots();
133
+ }
134
+
135
+ /**
136
+ * Render Timeslots by $post
137
+ */
138
+ public function append_time_slots() {
139
+ global $post;
140
+
141
+ $show_public_only = ( ( get_post_status( $post->ID ) == 'draft' ) && is_preview() ) ? false : true;
142
+
143
+ $data = $this->get_event_data( array( 'field' => 'event_id', 'id' => $post->ID ), 'event_start', $show_public_only );
144
+ $event_data = ( ! empty( $data ) ) ? $data : array();
145
+ $count = count( $event_data );
146
+
147
+ $this->get_view()->get_template( "theme/event-timeslots", array( 'events' => $event_data, 'count' => $count ) );
148
+ }
149
+
150
+ /**
151
+ * Render event options
152
+ *
153
+ * @param $post
154
+ */
155
+ public function render_event_options( $post ) {
156
+ $this->get_view()->render_html( "events/metabox-event-options", array( 'post' => $post ), true );
157
+ }
158
+
159
+ /**
160
+ * Add column
161
+ *
162
+ * @param $columns
163
+ *
164
+ * @return array
165
+ */
166
+ public function set_event_columns( $columns ) {
167
+ $columns = array_slice( $columns, 0, 2, true ) + array( $this->taxonomy_names[ 'tag' ] => __( 'Tags', 'mp-timetable' ) ) + array_slice( $columns, 2, count( $columns ) - 1, true );
168
+ $columns = array_slice( $columns, 0, 2, true ) + array( $this->taxonomy_names[ 'cat' ] => __( 'Categories', 'mp-timetable' ) ) + array_slice( $columns, 2, count( $columns ) - 1, true );
169
+
170
+ return $columns;
171
+ }
172
+
173
+ /**
174
+ * Create event category admin link
175
+ *
176
+ * @param $column
177
+ */
178
+ public function get_event_taxonomy( $column ) {
179
+ global $post;
180
+ if ( $column === $this->taxonomy_names[ 'cat' ] ) {
181
+ echo Taxonomy::get_instance()->get_the_term_filter_list( $post, $this->taxonomy_names[ 'cat' ] );
182
+ }
183
+ if ( $column === $this->taxonomy_names[ 'tag' ] ) {
184
+ echo Taxonomy::get_instance()->get_the_term_filter_list( $post, $this->taxonomy_names[ 'tag' ] );
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Output category post
190
+ *
191
+ * @param string $the_list
192
+ * @param string $separator
193
+ * @param string $parents
194
+ *
195
+ * @return mixed
196
+ */
197
+ public function the_category( $the_list = '', $separator = '', $parents = '' ) {
198
+ global $post;
199
+
200
+ if ( $post && $post->post_type === $this->post_type && ! is_admin() ) {
201
+ $categories = wp_get_post_terms( $post->ID, $this->taxonomy_names[ 'cat' ] );
202
+ $the_list .= $this->generate_event_tags( $categories, $separator, $parents );
203
+ }
204
+
205
+ /**
206
+ * Filter the category or list of Timetable categories.
207
+ *
208
+ * @param array $thelist List of categories for the current post.
209
+ * @param string $separator Separator used between the categories.
210
+ * @param string $parents How to display the category parents. Accepts 'multiple',
211
+ * 'single', or empty.
212
+ */
213
+ return apply_filters( 'mptt_the_category', $the_list, $separator, $parents );
214
+ }
215
+
216
+ /**
217
+ * Generate HTML of the post tags
218
+ *
219
+ * @param $categories
220
+ * @param $separator
221
+ * @param $parents
222
+ *
223
+ * @return string
224
+ */
225
+ public function generate_event_tags( $categories, $separator, $parents ) {
226
+ global $wp_rewrite;
227
+ $the_list = '';
228
+ $rel = ( is_object( $wp_rewrite ) && $wp_rewrite->using_permalinks() ) ? 'rel="category tag"' : 'rel="category"';
229
+
230
+ if ( '' == $separator ) {
231
+ $the_list .= '<ul class="post-categories">';
232
+ foreach ( $categories as $category ) {
233
+ $the_list .= "\n\t<li>";
234
+ switch ( strtolower( $parents ) ) {
235
+ case 'multiple':
236
+ if ( $category->parent ) {
237
+ $the_list .= get_category_parents( $category->parent, true, $separator );
238
+ }
239
+ $the_list .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a></li>';
240
+ break;
241
+ case 'single':
242
+ $the_list .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>';
243
+ if ( $category->parent ) {
244
+ $the_list .= get_category_parents( $category->parent, false, $separator );
245
+ }
246
+ $the_list .= $category->name . '</a></li>';
247
+ break;
248
+ case '':
249
+ default:
250
+ $the_list .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a></li>';
251
+ }
252
+ }
253
+ $the_list .= '</ul>';
254
+ } else {
255
+ $i = 0;
256
+ foreach ( $categories as $category ) {
257
+ if ( 0 < $i ) {
258
+ $the_list .= $separator;
259
+ }
260
+ switch ( strtolower( $parents ) ) {
261
+ case 'multiple':
262
+ if ( $category->parent ) {
263
+ $the_list .= get_category_parents( $category->parent, true, $separator );
264
+ }
265
+ $the_list .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a>';
266
+ break;
267
+ case 'single':
268
+ $the_list .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>';
269
+ if ( $category->parent ) {
270
+ $the_list .= get_category_parents( $category->parent, false, $separator );
271
+ }
272
+ $the_list .= "$category->name</a>";
273
+ break;
274
+ case '':
275
+ default:
276
+ $the_list .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name . '</a>';
277
+ }
278
+ ++ $i;
279
+ }
280
+ }
281
+
282
+ return $the_list;
283
+ }
284
+
285
+ /**
286
+ * Returns a formatted tags.
287
+ *
288
+ * @param $tags
289
+ * @param string $before
290
+ * @param string $sep
291
+ * @param string $after
292
+ * @param int $id
293
+ *
294
+ * @return mixed
295
+ */
296
+ public function the_tags( $tags, $before = '', $sep = '', $after = '', $id = 0 ) {
297
+ global $post;
298
+
299
+ if ( $post && $post->post_type === $this->post_type ) {
300
+ $id = ( $id === 0 ) ? $post->id : $id;
301
+ $events_tags = get_the_term_list( $id, $this->taxonomy_names[ 'tag' ], $before, $sep, $after );
302
+ $tags = apply_filters( 'mptt_the_tags', $events_tags, $tags );
303
+ }
304
+
305
+ return $tags;
306
+ }
307
+
308
+ /**
309
+ * Save(insert) event data
310
+ *
311
+ * @param array $params
312
+ *
313
+ * @return array
314
+ */
315
+ public function save_event_data( array $params ) {
316
+ $rows_affected = array();
317
+ if ( ! empty( $params[ 'event_data' ] ) ) {
318
+ foreach ( $params[ 'event_data' ] as $key => $event ) {
319
+ if ( is_array( $event[ 'event_start' ] ) && ! empty( $event[ 'event_start' ] ) ) {
320
+
321
+ for ( $i = 0; $i < count( $event[ 'event_start' ] ); $i ++ ) {
322
+ $rows_affected[] = $this->wpdb->insert( $this->table_name, array(
323
+ 'column_id' => $key,
324
+ 'event_id' => $params[ 'post' ]->ID,
325
+ 'event_start' => date( 'H:i', strtotime( $event[ 'event_start' ][ $i ] ) ),
326
+ 'event_end' => date( 'H:i', strtotime( $event[ 'event_end' ][ $i ] ) ),
327
+ 'user_id' => $event[ 'user_id' ][ $i ],
328
+ 'description' => $event[ 'description' ][ $i ]
329
+ )
330
+ );
331
+ }
332
+ }
333
+ }
334
+ }
335
+ if ( ! empty( $params[ 'event_meta' ] ) ) {
336
+ foreach ( $params[ 'event_meta' ] as $meta_key => $meta ) {
337
+ update_post_meta( $params[ 'post' ]->ID, $meta_key, $meta );
338
+ }
339
+ }
340
+
341
+ return $rows_affected;
342
+ }
343
+
344
+ /**
345
+ * Delete event timeslot
346
+ *
347
+ * @param $id
348
+ *
349
+ * @return false|int
350
+ */
351
+ public function delete_event( $id ) {
352
+ return $this->wpdb->delete( $this->table_name, array( 'id' => $id ), array( '%d' ) );
353
+ }
354
+
355
+ /**
356
+ * Delete event data
357
+ *
358
+ * @param $post_id
359
+ *
360
+ * @return false|int
361
+ */
362
+ public function before_delete_event( $post_id ) {
363
+ $meta_keys = array( 'event_id', 'event_start', 'event_end', 'user_id', 'description' );
364
+
365
+ foreach ( $meta_keys as $meta_key ) {
366
+ delete_post_meta( $post_id, $meta_key );
367
+ }
368
+
369
+ return $this->wpdb->delete( $this->table_name, array( 'event_id' => $post_id ), array( '%d' ) );
370
+ }
371
+
372
+ /**
373
+ * Get widget events
374
+ *
375
+ * @param $instance
376
+ *
377
+ * @return array
378
+ */
379
+ public function get_widget_events( $instance ) {
380
+ $events = array();
381
+ $current_local_time = current_time( 'timestamp' );
382
+
383
+ $weekday = strtolower( date( 'l', $current_local_time ) );
384
+ $current_date = date( 'd/m/Y', $current_local_time );
385
+
386
+ // 24.09.2019 seconds added
387
+ $curent_time = date( 'H:i:s', $current_local_time );
388
+
389
+ if ( ! empty( $instance[ 'mp_categories' ] ) ) {
390
+ $category_columns_ids = $this->get( 'column' )->get_columns_by_event_category( $instance[ 'mp_categories' ] );
391
+ }
392
+
393
+ $args = array(
394
+ 'post_type' => 'mp-column',
395
+ 'post_status' => 'publish',
396
+ 'fields' => 'ids',
397
+ 'post__in' => ! empty( $category_columns_ids ) ? $category_columns_ids : '',
398
+ 'orderby' => 'menu_order',
399
+ 'meta_query' => array(
400
+ 'relation' => 'OR',
401
+ array(
402
+ 'key' => 'weekday',
403
+ 'value' => $weekday
404
+ ),
405
+ array(
406
+ 'key' => 'option_day',
407
+ 'value' => $current_date
408
+ )
409
+ )
410
+ );
411
+
412
+ switch ( $instance[ 'view_settings' ] ) {
413
+ case'today':
414
+ case 'current':
415
+ $column_post_ids = get_posts( $args );
416
+ if ( ! empty( $column_post_ids ) ) {
417
+ $events = $this->get_events_data( array( 'column' => 'column_id', 'list' => $column_post_ids ) );
418
+ }
419
+ $events = $this->filter_events( array( 'events' => $events, 'view_settings' => $instance[ 'view_settings' ], 'time' => $curent_time, 'mp_categories' => $instance[ 'mp_categories' ] ) );
420
+ break;
421
+ case 'all':
422
+
423
+ if ( ! empty( $instance[ 'next_days' ] ) && $instance[ 'next_days' ] > 0 ) {
424
+ $events_array = array();
425
+ for ( $i = 0; $i <= $instance[ 'next_days' ]; $i ++ ) {
426
+
427
+ // set new day week
428
+ $time = strtotime( "+$i days", $current_local_time );
429
+ $day = strtolower( date( 'l', $time ) );
430
+ $date = date( 'd/m/Y', $time );
431
+
432
+ //set week day
433
+ $args[ 'meta_query' ][ 0 ][ 'value' ] = $day;
434
+ //set new date
435
+ $args[ 'meta_query' ][ 1 ][ 'value' ] = $date;
436
+
437
+ $column_post_ids = get_posts( $args );
438
+
439
+ if ( ! empty( $column_post_ids ) ) {
440
+ $events_array[ $i ] = $this->get_events_data( array( 'column' => 'column_id', 'list' => $column_post_ids ) );
441
+ } else {
442
+ $events_array[ $i ] = array();
443
+ }
444
+
445
+ // Filter by time and categories for current day
446
+ if ( $i === 0 && ! empty( $instance[ 'mp_categories' ] ) && ! empty( $events_array[ $i ] ) ) {
447
+ $events_array[ $i ] = $events_array[ $i ] = $this->filter_events( array( 'events' => $events_array[ $i ], 'view_settings' => 'today', 'time' => $curent_time, 'mp_categories' => $instance[ 'mp_categories' ] ) );
448
+ } elseif ( ! empty( $instance[ 'mp_categories' ] ) && ! empty( $events_array[ $i ] ) ) {
449
+ //Filter by categories for next days
450
+ $events_array[ $i ] = $this->filter_events_by_categories( $events_array[ $i ], $instance[ 'mp_categories' ] );
451
+ }
452
+
453
+ }
454
+
455
+ foreach ( $events_array as $day_events ) {
456
+ $events = array_merge( $events, $day_events );
457
+ }
458
+
459
+ }
460
+
461
+ break;
462
+
463
+ default:
464
+ $column_post_ids = get_posts( $args );
465
+ if ( ! empty( $column_post_ids ) ) {
466
+ $events = $this->get_events_data( array( 'column' => 'column_id', 'list' => $column_post_ids ) );
467
+ }
468
+ $events = $this->filter_events( array( 'events' => $events, 'view_settings' => 'today', 'time' => $curent_time, 'mp_categories' => $instance[ 'mp_categories' ] ) );
469
+
470
+ break;
471
+ }
472
+ if ( $instance[ 'limit' ] > 0 ) {
473
+ $events = array_slice( $events, 0, $instance[ 'limit' ] );
474
+ }
475
+
476
+ return $events;
477
+ }
478
+
479
+ /**
480
+ * Get event data by post
481
+ *
482
+ * @param array $params
483
+ *
484
+ * @return array|null|object
485
+ */
486
+ public function get_events_data( array $params ) {
487
+ $events = array();
488
+ $sql_reguest = "SELECT * FROM " . $this->table_name;
489
+
490
+ if ( ( ! empty( $params[ 'all' ] ) && $params[ 'all' ] ) || empty( $params[ 'list' ] ) ) {
491
+
492
+ } elseif ( ! is_array( $params[ 'column' ] ) ) {
493
+
494
+ if ( isset( $params[ 'list' ] ) && is_array( $params[ 'list' ] ) ) {
495
+ $params[ 'list' ] = implode( ',', $params[ 'list' ] );
496
+ }
497
+
498
+ $sql_reguest .= " WHERE " . $params[ 'column' ] . " IN (" . $params[ 'list' ] . ")";
499
+
500
+ } elseif ( is_array( $params[ 'column' ] ) && is_array( $params[ 'column' ] ) ) {
501
+
502
+ $sql_reguest .= " WHERE ";
503
+
504
+ $last_key = key( array_slice( $params[ 'column' ], - 1, 1, true ) );
505
+
506
+ foreach ( $params[ 'column' ] as $key => $column ) {
507
+ if ( isset( $params[ 'list' ][ $column ] ) && is_array( $params[ 'list' ][ $column ] ) ) {
508
+ $params[ 'list' ][ $column ] = implode( ',', $params[ 'list' ][ $column ] );
509
+ }
510
+ $sql_reguest .= $column . " IN (" . $params[ 'list' ][ $column ] . ")";
511
+ $sql_reguest .= ( $last_key != $key ) ? ' AND ' : '';
512
+ }
513
+
514
+ }
515
+
516
+ $sql_reguest .= ' ORDER BY `event_start`';
517
+
518
+ $events_data = $this->wpdb->get_results( $sql_reguest );
519
+
520
+ if ( is_array( $events_data ) ) {
521
+
522
+ foreach ( $events_data as $event ) {
523
+ $post = get_post( $event->event_id );
524
+
525
+ if ( $post && ( $post->post_type == $this->post_type ) && ( $post->post_status == 'publish' ) ) {
526
+ $event->post = $post;
527
+ $event->event_start = date( 'H:i', strtotime( $event->event_start ) );
528
+ $event->event_end = date( 'H:i', strtotime( $event->event_end ) );
529
+ $events[] = $event;
530
+ }
531
+ }
532
+ }
533
+
534
+ return $events;
535
+ }
536
+
537
+ /**
538
+ * Filtered events by view settings
539
+ *
540
+ * @param $params
541
+ *
542
+ * @return array
543
+ */
544
+ protected function filter_events( $params ) {
545
+
546
+ $events = array();
547
+ $events = $this->filter_by_time_period( $params, $events );
548
+
549
+ if ( ! empty( $params[ 'mp_categories' ] ) ) {
550
+ $events = $this->filter_events_by_categories( $events, $params[ 'mp_categories' ] );
551
+ }
552
+
553
+ return $events;
554
+ }
555
+
556
+ /**
557
+ * Filter by time period
558
+ *
559
+ * @param $params
560
+ * @param $events
561
+ *
562
+ * @return array
563
+ */
564
+ protected function filter_by_time_period( $params, $events ) {
565
+ if ( ! empty( $params[ 'events' ] ) ) {
566
+
567
+ $time = strtotime( $params[ 'time' ] );
568
+
569
+ foreach ( $params[ 'events' ] as $key => $event ) {
570
+
571
+ $event_end = strtotime( $event->event_end );
572
+ $event_start = strtotime( $event->event_start );
573
+
574
+ if ( $params[ 'view_settings' ] === 'today' || $params[ 'view_settings' ] === 'all' ) {
575
+ if ( $event_end <= $time ) {
576
+ continue;
577
+ }
578
+ } elseif ( $params[ 'view_settings' ] === 'current' ) {
579
+ if ( $event_start >= $time || $event_end <= $time ) {
580
+ continue;
581
+ }
582
+ }
583
+ $events[ $key ] = $event;
584
+ }
585
+ }
586
+
587
+ return $events;
588
+ }
589
+
590
+ /**
591
+ * Filter find events by select categories;
592
+ *
593
+ * @param array $events
594
+ * @param array $categories
595
+ *
596
+ * @return array
597
+ */
598
+ public function filter_events_by_categories( array $events, array $categories ) {
599
+ $temp_events = array();
600
+ $taxonomy = $this->taxonomy_names[ 'cat' ];
601
+
602
+ foreach ( $events as $event ) {
603
+ if ( @has_term( $categories, $taxonomy, $event->post->ID ) ) {
604
+ $temp_events[] = $event;
605
+ }
606
+ }
607
+
608
+ return $temp_events;
609
+ }
610
+
611
+ /**
612
+ * Sort by params
613
+ *
614
+ * @param $events
615
+ *
616
+ * @return mixed
617
+ */
618
+
619
+ public function sort_by_param( $events ) {
620
+
621
+ usort( $events, function ( $a, $b ) {
622
+ if ( strtotime( $a->event_start ) == strtotime( $b->event_start ) ) {
623
+ return 0;
624
+ }
625
+
626
+ return ( strtotime( $a->event_start ) < strtotime( $b->event_start ) ) ? - 1 : 1;
627
+ } );
628
+
629
+
630
+ return $events;
631
+ }
632
+
633
+ /**
634
+ * @param array /string $event_category
635
+ *
636
+ * @return array|null|object
637
+ */
638
+ public function get_events_data_by_category( $event_categories ) {
639
+ if ( ! is_array( $event_categories ) ) {
640
+ $terms = explode( ',', $event_categories );
641
+ } else {
642
+ $terms = $event_categories;
643
+ }
644
+
645
+ $posts_array = get_posts(
646
+ array(
647
+ 'fields' => 'ids',
648
+ 'posts_per_page' => - 1,
649
+ 'post_type' => $this->post_type,
650
+ 'post_status' => 'publish',
651
+ 'tax_query' => array(
652
+ array(
653
+ 'taxonomy' => 'mp-event_category',
654
+ 'field' => 'term_id',
655
+ 'terms' => $terms,
656
+ )
657
+ )
658
+ )
659
+ );
660
+
661
+ $ids = implode( ',', $posts_array );
662
+ $event_data = $this->get_events_data( array( 'column' => 'event_id', 'list' => $ids ) );
663
+
664
+ return $event_data;
665
+ }
666
+
667
+ /**
668
+ * Update event data
669
+ *
670
+ * @param $data
671
+ *
672
+ * @return false|int
673
+ */
674
+ public function update_event_data( $data ) {
675
+ $result = $this->wpdb->update(
676
+ $this->table_name,
677
+ array(
678
+ 'event_start' => date( 'H:i', strtotime( $data[ 'event_start' ] ) ),
679
+ 'event_end' => date( 'H:i', strtotime( $data[ 'event_end' ] ) ),
680
+ 'description' => $data[ 'description' ],
681
+ 'column_id' => $data[ 'weekday_ids' ],
682
+ 'user_id' => $data[ 'user_id' ],
683
+ ),
684
+ array( 'id' => $data[ 'id' ] ),
685
+ array(
686
+ '%s',
687
+ '%s',
688
+ '%s',
689
+ '%d',
690
+ '%d',
691
+ ),
692
+ array( '%d' )
693
+ );
694
+
695
+ return $result;
696
+ }
697
+
698
+ /**
699
+ * Get all events
700
+ * @return array
701
+ */
702
+ public function get_all_events() {
703
+ $args = array(
704
+ 'post_type' => $this->post_type,
705
+ 'post_status' => 'publish',
706
+ 'order' => 'ASC',
707
+ 'orderby' => 'title',
708
+ 'posts_per_page' => - 1
709
+ );
710
+
711
+ return get_posts( $args );
712
+ }
713
+
714
+ /**
715
+ * Choose color widget or event
716
+ *
717
+ * @param $params
718
+ *
719
+ * @return string
720
+ */
721
+
722
+ public function choose_event_color( $params ) {
723
+ if ( ! empty( $params[ 'widget_color' ] ) ) {
724
+ return $params[ 'widget_color' ];
725
+ } elseif ( ! empty( $params[ 'event_color' ] ) ) {
726
+ return $params[ 'event_color' ];
727
+ } else {
728
+ return '';
729
+ }
730
+ }
731
+
732
+ /**
733
+ * Filtered events by Event Head
734
+ *
735
+ * @param $params
736
+ *
737
+ * @return array
738
+ */
739
+ protected function filter_events_by_field( $params ) {
740
+ $events = array();
741
+ if ( ! empty( $params[ 'events' ] ) ) {
742
+ foreach ( $params[ 'events' ] as $key => $event ) {
743
+ if ( $event->$params[ 'field' ] != $params[ 'value' ] ) {
744
+ continue;
745
+ }
746
+
747
+ $events[ $key ] = $event;
748
+ }
749
+ }
750
+
751
+ return $events;
752
+ }
753
+ }
classes/models/class-export.php ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\models;
3
+
4
+ use mp_timetable\classes\libs;
5
+ use mp_timetable\plugin_core\classes\Model as Model;
6
+
7
+ /**
8
+ * Export model
9
+ */
10
+ class Export extends Model {
11
+ protected static $instance;
12
+ private $table;
13
+
14
+ public static function get_instance() {
15
+ if (null === self::$instance) {
16
+ self::$instance = new self();
17
+ }
18
+ return self::$instance;
19
+ }
20
+
21
+ function __construct() {
22
+ $this->table = Events::get_instance()->table_name;
23
+ parent::__construct();
24
+ }
25
+
26
+ /**
27
+ * Export file
28
+ */
29
+ public function export() {
30
+ global $wpdb, $post;
31
+
32
+ if (!defined('ABSPATH')) exit;
33
+
34
+ if (!function_exists('export_wp')) {
35
+ include_once(ABSPATH . 'wp-admin/includes/export.php');
36
+ }
37
+ $args = array('content' => $this->post_types, 'author' => false, 'category' => false,
38
+ 'start_date' => false, 'end_date' => false, 'status' => false);
39
+
40
+
41
+ $defaults = array('content' => 'all', 'author' => false, 'category' => false,
42
+ 'start_date' => false, 'end_date' => false, 'status' => false,
43
+ );
44
+ $args = wp_parse_args($args, $defaults);
45
+
46
+ /**
47
+ * Fires at the beginning of an export, before any headers are sent.
48
+ *
49
+ * @since 2.3.0
50
+ *
51
+ * @param array $args An array of export arguments.
52
+ */
53
+ do_action('export_wp', $args);
54
+
55
+ $filename = $this->file_name();
56
+
57
+ header('Content-Description: File Transfer');
58
+ header('Content-Disposition: attachment; filename=' . $filename);
59
+ header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
60
+
61
+ if ('all' != $args['content'] && post_type_exists($args['content'])) {
62
+ $ptype = get_post_type_object($args['content']);
63
+ if (!$ptype->can_export)
64
+ $args['content'] = 'post';
65
+
66
+ $where = $wpdb->prepare("{$wpdb->posts}.post_type = %s", $args['content']);
67
+ } else {
68
+ $post_types = get_post_types(array('can_export' => true));
69
+ $post_types = array_intersect($post_types, $args['content']);
70
+ $esses = array_fill(0, count($post_types), '%s');
71
+ $where = $wpdb->prepare("{$wpdb->posts}.post_type IN (" . implode(',', $esses) . ')', $post_types);
72
+ }
73
+
74
+ if ($args['status'] && ('post' == $args['content'] || 'page' == $args['content']))
75
+ $where .= $wpdb->prepare(" AND {$wpdb->posts}.post_status = %s", $args['status']);
76
+ else
77
+ $where .= " AND {$wpdb->posts}.post_status != 'auto-draft'";
78
+
79
+ $join = '';
80
+ if ($args['category'] && 'post' == $args['content']) {
81
+ if ($term = term_exists($args['category'], 'category')) {
82
+ $join = "INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
83
+ $where .= $wpdb->prepare(" AND {$wpdb->term_relationships}.term_taxonomy_id = %d", $term['term_taxonomy_id']);
84
+ }
85
+ }
86
+
87
+ if ('post' == $args['content'] || 'page' == $args['content'] || 'attachment' == $args['content']) {
88
+ if ($args['author'])
89
+ $where .= $wpdb->prepare(" AND {$wpdb->posts}.post_author = %d", $args['author']);
90
+
91
+ if ($args['start_date'])
92
+ $where .= $wpdb->prepare(" AND {$wpdb->posts}.post_date >= %s", date('Y-m-d', strtotime($args['start_date'])));
93
+
94
+ if ($args['end_date'])
95
+ $where .= $wpdb->prepare(" AND {$wpdb->posts}.post_date < %s", date('Y-m-d', strtotime('+1 month', strtotime($args['end_date']))));
96
+ }
97
+
98
+ // Grab a snapshot of post IDs, just in case it changes during the export.
99
+ $post_ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} $join WHERE $where");
100
+
101
+ /*
102
+ * Get the requested terms ready, empty unless posts filtered by category
103
+ * or all content.
104
+ */
105
+ $thumbnail_ids = array();
106
+ foreach ($post_ids as $ID) {
107
+ $thumbnail_id = get_post_thumbnail_id($ID);
108
+ if (empty($thumbnail_id)) {
109
+ continue;
110
+ }
111
+ $thumbnail_ids[] = $thumbnail_id;
112
+ }
113
+ if (!empty($thumbnail_ids)) {
114
+ $post_ids = array_merge($post_ids, $thumbnail_ids);
115
+ }
116
+ $cats = $tags = $terms = array();
117
+
118
+ if (!empty($this->taxonomy_names)) {
119
+ $custom_taxonomies = get_taxonomies(array('_builtin' => false));
120
+ $custom_terms = (array)get_terms($this->taxonomy_names, array('get' => 'all'));
121
+
122
+ // Put terms in order with no child going before its parent.
123
+ while ($t = array_shift($custom_terms)) {
124
+ if ($t->parent == 0 || isset($terms[$t->parent]))
125
+ $terms[$t->term_id] = $t;
126
+ else
127
+ $custom_terms[] = $t;
128
+ }
129
+
130
+ unset($categories, $custom_taxonomies, $custom_terms);
131
+ }
132
+
133
+ add_filter('mptt_export_skip_postmeta', array($this, 'mptt_filter_postmeta'), 10, 2);
134
+
135
+ echo '<?xml version="1.0" encoding="' . get_bloginfo('charset') . "\" ?>\n"; ?>
136
+ <?php the_generator('export'); ?>
137
+
138
+ <rss version="2.0"
139
+ xmlns:excerpt="http://wordpress.org/export/<?php echo WXR_VERSION; ?>/excerpt/"
140
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
141
+ xmlns:wfw="http://wellformedweb.org/CommentAPI/"
142
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
143
+ xmlns:wp="http://wordpress.org/export/<?php echo WXR_VERSION; ?>/">
144
+ <channel>
145
+ <title><?php bloginfo_rss('name'); ?></title>
146
+ <link><?php bloginfo_rss('url'); ?></link>
147
+ <description><?php bloginfo_rss('description'); ?></description>
148
+ <pubDate><?php echo date('D, d M Y H:i:s +0000'); ?></pubDate>
149
+ <language><?php bloginfo_rss('language'); ?></language>
150
+ <wp:wxr_version><?php echo WXR_VERSION; ?></wp:wxr_version>
151
+ <wp:base_site_url><?php echo $this->mptt_site_url(); ?></wp:base_site_url>
152
+ <wp:base_blog_url><?php bloginfo_rss('url'); ?></wp:base_blog_url>
153
+
154
+ <?php $this->mptt_authors_list($post_ids); ?>
155
+ <?php foreach ($terms as $t) : ?>
156
+ <wp:term>
157
+ <wp:term_id><?php echo $this->mptt_cdata($t->term_id); ?></wp:term_id>
158
+ <wp:term_taxonomy><?php echo $this->mptt_cdata($t->taxonomy); ?></wp:term_taxonomy>
159
+ <wp:term_slug><?php echo $this->mptt_cdata($t->slug); ?></wp:term_slug>
160
+ <wp:term_parent><?php echo $this->mptt_cdata($t->parent ? $terms[$t->parent]->slug : ''); ?></wp:term_parent><?php $this->mptt_term_name($t); ?><?php $this->mptt_term_description($t); ?> </wp:term>
161
+ <?php endforeach; ?>
162
+
163
+
164
+ <?php
165
+ /** This action is documented in wp-includes/feed-rss2.php */
166
+ do_action('rss2_head');
167
+ ?>
168
+
169
+ <?php if ($post_ids) {
170
+ /**
171
+ * @global WP_Query $wp_query
172
+ */
173
+ global $wp_query;
174
+
175
+ // Fake being in the loop.
176
+ $wp_query->in_the_loop = true;
177
+
178
+ // Fetch 20 posts at a time rather than loading the entire table into memory.
179
+ while ($next_posts = array_splice($post_ids, 0, 20)) {
180
+ $where = 'WHERE ID IN (' . join(',', $next_posts) . ')';
181
+ $posts = $wpdb->get_results("SELECT * FROM {$wpdb->posts} $where");
182
+
183
+ // Begin Loop.
184
+ foreach ($posts as $post) {
185
+ setup_postdata($post);
186
+ $is_sticky = is_sticky($post->ID) ? 1 : 0; ?>
187
+ <item>
188
+ <title><?php
189
+ /** This filter is documented in wp-includes/feed.php */
190
+ echo apply_filters('the_title_rss', $post->post_title); ?></title>
191
+ <link><?php the_permalink_rss() ?></link>
192
+ <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_post_time('Y-m-d H:i:s', true), false); ?></pubDate>
193
+ <dc:creator><?php echo $this->mptt_cdata(get_the_author_meta('login')); ?></dc:creator>
194
+ <guid isPermaLink="false"><?php the_guid(); ?></guid>
195
+ <description></description>
196
+ <content:encoded><?php
197
+ /**
198
+ * Filter the post content used for WXR exports.
199
+ *
200
+ * @since 2.5.0
201
+ *
202
+ * @param string $post_content Content of the current post.
203
+ */
204
+ echo $this->mptt_cdata(apply_filters('the_content_export', $post->post_content));
205
+ ?></content:encoded>
206
+ <excerpt:encoded><?php
207
+ /**
208
+ * Filter the post excerpt used for WXR exports.
209
+ *
210
+ * @since 2.6.0
211
+ *
212
+ * @param string $post_excerpt Excerpt for the current post.
213
+ */
214
+ echo $this->mptt_cdata(apply_filters('the_excerpt_export', $post->post_excerpt));
215
+ ?></excerpt:encoded>
216
+ <wp:post_id><?php echo intval($post->ID); ?></wp:post_id>
217
+ <wp:post_date><?php echo $this->mptt_cdata($post->post_date); ?></wp:post_date>
218
+ <wp:post_date_gmt><?php echo $this->mptt_cdata($post->post_date_gmt); ?></wp:post_date_gmt>
219
+ <wp:comment_status><?php echo $this->mptt_cdata($post->comment_status); ?></wp:comment_status>
220
+ <wp:ping_status><?php echo $this->mptt_cdata($post->ping_status); ?></wp:ping_status>
221
+ <wp:post_name><?php echo $this->mptt_cdata($post->post_name); ?></wp:post_name>
222
+ <wp:status><?php echo $this->mptt_cdata($post->post_status); ?></wp:status>
223
+ <wp:post_parent><?php echo intval($post->post_parent); ?></wp:post_parent>
224
+ <wp:menu_order><?php echo intval($post->menu_order); ?></wp:menu_order>
225
+ <wp:post_type><?php echo $this->mptt_cdata($post->post_type); ?></wp:post_type>
226
+ <wp:post_password><?php echo $this->mptt_cdata($post->post_password); ?></wp:post_password>
227
+ <wp:is_sticky><?php echo intval($is_sticky); ?></wp:is_sticky>
228
+ <?php if ($post->post_type == 'attachment') : ?>
229
+ <wp:attachment_url><?php echo $this->mptt_cdata(wp_get_attachment_url($post->ID)); ?></wp:attachment_url>
230
+ <?php endif; ?>
231
+ <?php $this->mptt_post_taxonomy(); ?>
232
+
233
+ <?php $postmeta = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID));
234
+ foreach ($postmeta as $meta) :
235
+ /**
236
+ * Filter whether to selectively skip post meta used for WXR exports.
237
+ *
238
+ * Returning a truthy value to the filter will skip the current meta
239
+ * object from being exported.
240
+ *
241
+ * @since 3.3.0
242
+ *
243
+ * @param bool $skip Whether to skip the current post meta. Default false.
244
+ * @param string $meta_key Current meta key.
245
+ * @param object $meta Current meta object.
246
+ */
247
+ if (apply_filters('mptt_export_skip_postmeta', false, $meta->meta_key, $meta))
248
+ continue;
249
+ ?>
250
+ <wp:postmeta>
251
+ <wp:meta_key><?php echo $this->mptt_cdata($meta->meta_key); ?></wp:meta_key>
252
+ <wp:meta_value><?php echo $this->mptt_cdata($meta->meta_value); ?></wp:meta_value>
253
+ </wp:postmeta>
254
+ <?php endforeach; ?>
255
+ <?php
256
+
257
+ $_comments = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID));
258
+ $comments = array_map('get_comment', $_comments);
259
+ foreach ($comments as $c) : ?>
260
+ <wp:comment>
261
+ <wp:comment_id><?php echo intval($c->comment_ID); ?></wp:comment_id>
262
+ <wp:comment_author><?php echo $this->mptt_cdata($c->comment_author); ?></wp:comment_author>
263
+ <wp:comment_author_email><?php echo $this->mptt_cdata($c->comment_author_email); ?></wp:comment_author_email>
264
+ <wp:comment_author_url><?php echo esc_url_raw($c->comment_author_url); ?></wp:comment_author_url>
265
+ <wp:comment_author_IP><?php echo $this->mptt_cdata($c->comment_author_IP); ?></wp:comment_author_IP>
266
+ <wp:comment_date><?php echo $this->mptt_cdata($c->comment_date); ?></wp:comment_date>
267
+ <wp:comment_date_gmt><?php echo $this->mptt_cdata($c->comment_date_gmt); ?></wp:comment_date_gmt>
268
+ <wp:comment_content><?php echo $this->mptt_cdata($c->comment_content) ?></wp:comment_content>
269
+ <wp:comment_approved><?php echo $this->mptt_cdata($c->comment_approved); ?></wp:comment_approved>
270
+ <wp:comment_type><?php echo $this->mptt_cdata($c->comment_type); ?></wp:comment_type>
271
+ <wp:comment_parent><?php echo intval($c->comment_parent); ?></wp:comment_parent>
272
+ <wp:comment_user_id><?php echo intval($c->user_id); ?></wp:comment_user_id>
273
+ <?php $c_meta = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $c->comment_ID));
274
+ foreach ($c_meta as $meta) :
275
+ /**
276
+ * Filter whether to selectively skip comment meta used for WXR exports.
277
+ *
278
+ * Returning a truthy value to the filter will skip the current meta
279
+ * object from being exported.
280
+ *
281
+ * @since 4.0.0
282
+ *
283
+ * @param bool $skip Whether to skip the current comment meta. Default false.
284
+ * @param string $meta_key Current meta key.
285
+ * @param object $meta Current meta object.
286
+ */
287
+ if (apply_filters('mptt_export_skip_commentmeta', false, $meta->meta_key, $meta)) {
288
+ continue;
289
+ }
290
+ ?>
291
+ <wp:commentmeta>
292
+ <wp:meta_key><?php echo $this->mptt_cdata($meta->meta_key); ?></wp:meta_key>
293
+ <wp:meta_value><?php echo $this->mptt_cdata($meta->meta_value); ?></wp:meta_value>
294
+ </wp:commentmeta>
295
+ <?php endforeach; ?>
296
+ </wp:comment>
297
+ <?php endforeach; ?>
298
+ </item>
299
+ <?php
300
+ }
301
+ }
302
+
303
+ }
304
+ $time_slots = $wpdb->get_results("SELECT * FROM $this->table WHERE 1");
305
+ if (!empty($time_slots)) {
306
+ foreach ($time_slots as $time_slot) : ?>
307
+ <timeslot>
308
+ <column><?php echo $this->mptt_cdata($time_slot->column_id); ?></column>
309
+ <event><?php echo $this->mptt_cdata($time_slot->event_id); ?></event>
310
+ <event_start><?php echo $this->mptt_cdata($time_slot->event_start); ?></event_start>
311
+ <event_end><?php echo $this->mptt_cdata($time_slot->event_end); ?></event_end>
312
+ <user_id><?php echo $this->mptt_cdata($time_slot->user_id); ?></user_id>
313
+ <description><?php echo $this->mptt_cdata($time_slot->description); ?></description>
314
+ </timeslot>
315
+ <?php endforeach;
316
+ }
317
+ ?>
318
+ </channel>
319
+ </rss>
320
+ <?php
321
+ }
322
+
323
+ /**
324
+ * Generate file name
325
+ * @return mixed|void
326
+ */
327
+ public function file_name() {
328
+ $sitename = sanitize_key(get_bloginfo('name'));
329
+ if (!empty($sitename)) {
330
+ $sitename .= '_';
331
+ }
332
+ $date = date('d.m.Y_H.i', time());
333
+
334
+ $wp_filename = $sitename . 'timetable_' . $date . '.xml';
335
+ /**
336
+ * Filter the export filename.
337
+ *
338
+ * @since 4.4.0
339
+ *
340
+ * @param string $wp_filename The name of the file for download.
341
+ * @param string $sitename The site name.
342
+ * @param string $date Today's date, formatted.
343
+ */
344
+ $filename = apply_filters('export_wp_filename', $wp_filename, $sitename, $date);
345
+ return $filename;
346
+ }
347
+
348
+ /**
349
+ * Output list of authors with posts
350
+ *
351
+ * @since 3.1.0
352
+ *
353
+ * @global wpdb $wpdb WordPress database abstraction object.
354
+ *
355
+ * @param array $post_ids Array of post IDs to filter the query by. Optional.
356
+ */
357
+ public function mptt_authors_list(array $post_ids = null) {
358
+ global $wpdb;
359
+
360
+ if (!empty($post_ids)) {
361
+ $post_ids = array_map('absint', $post_ids);
362
+ $and = 'AND ID IN ( ' . implode(', ', $post_ids) . ')';
363
+ } else {
364
+ $and = '';
365
+ }
366
+
367
+ $authors = array();
368
+ $results = $wpdb->get_results("SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft' $and");
369
+ foreach ((array)$results as $result)
370
+ $authors[] = get_userdata($result->post_author);
371
+
372
+ $authors = array_filter($authors);
373
+
374
+ foreach ($authors as $author) {
375
+ echo "\t<wp:author>";
376
+ echo '<wp:author_id>' . intval($author->ID) . '</wp:author_id>';
377
+ echo '<wp:author_login>' . $this->mptt_cdata($author->user_login) . '</wp:author_login>';
378
+ echo '<wp:author_email>' . $this->mptt_cdata($author->user_email) . '</wp:author_email>';
379
+ echo '<wp:author_display_name>' . $this->mptt_cdata($author->display_name) . '</wp:author_display_name>';
380
+ echo '<wp:author_first_name>' . $this->mptt_cdata($author->first_name) . '</wp:author_first_name>';
381
+ echo '<wp:author_last_name>' . $this->mptt_cdata($author->last_name) . '</wp:author_last_name>';
382
+ echo "</wp:author>\n";
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Return the URL of the site
388
+ *
389
+ * @since 2.5.0
390
+ *
391
+ * @return string Site URL.
392
+ */
393
+ public function mptt_site_url() {
394
+ // Multisite: the base URL.
395
+ if (is_multisite())
396
+ return network_home_url();
397
+ // WordPress (single site): the blog URL.
398
+ else
399
+ return get_bloginfo_rss('url');
400
+ }
401
+
402
+ /**
403
+ * Output a term_description XML tag from a given term object
404
+ *
405
+ * @since 2.9.0
406
+ *
407
+ * @param object $term Term Object
408
+ */
409
+ public function mptt_term_description($term) {
410
+ if (empty($term->description))
411
+ return;
412
+
413
+ echo '<wp:term_description>' . $this->mptt_cdata($term->description) . '</wp:term_description>';
414
+ }
415
+
416
+ /**
417
+ * Output a term_name XML tag from a given term object
418
+ *
419
+ * @since 2.9.0
420
+ *
421
+ * @param object $term Term Object
422
+ */
423
+ public function mptt_term_name($term) {
424
+ if (empty($term->name))
425
+ return;
426
+
427
+ echo '<wp:term_name>' . $this->mptt_cdata($term->name) . '</wp:term_name>';
428
+ }
429
+
430
+ /**
431
+ * Wrap given string in XML CDATA tag.
432
+ *
433
+ * @since 2.1.0
434
+ *
435
+ * @param string $str String to wrap in XML CDATA tag.
436
+ *
437
+ * @return string
438
+ */
439
+ public function mptt_cdata($str) {
440
+ if (!seems_utf8($str)) {
441
+ $str = utf8_encode($str);
442
+ }
443
+ // $str = ent2ncr(esc_html($str));
444
+ $str = '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $str) . ']]>';
445
+
446
+ return $str;
447
+ }
448
+
449
+ /**
450
+ *
451
+ * @param bool $return_me
452
+ * @param string $meta_key
453
+ *
454
+ * @return bool
455
+ */
456
+ public function mptt_filter_postmeta($return_me, $meta_key) {
457
+ if ('_edit_lock' == $meta_key)
458
+ $return_me = true;
459
+ return $return_me;
460
+ }
461
+
462
+ /**
463
+ * Output list of taxonomy terms, in XML tag format, associated with a post
464
+ *
465
+ * @since 2.3.0
466
+ */
467
+ public function mptt_post_taxonomy() {
468
+ $post = get_post();
469
+
470
+ $taxonomies = get_object_taxonomies($post->post_type);
471
+ if (empty($taxonomies))
472
+ return;
473
+ $terms = wp_get_object_terms($post->ID, $taxonomies);
474
+
475
+ foreach ((array)$terms as $term) {
476
+ echo "\t\t<category domain=\"{$term->taxonomy}\" nicename=\"{$term->slug}\">" . $this->mptt_cdata($term->name) . "</category>\n";
477
+ }
478
+ }
479
+ }
classes/models/class-import.php ADDED
@@ -0,0 +1,968 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace mp_timetable\classes\models;
3
+
4
+ use mp_timetable\classes\libs;
5
+ use mp_timetable\plugin_core\classes\Model as Model;
6
+ use mp_timetable\plugin_core\classes\View as View;
7
+
8
+ /**
9
+ * Model Events
10
+ */
11
+ class Import extends Model {
12
+
13
+ protected static $instance;
14
+ protected $table;
15
+ var $max_wxr_version = 1.2; // max. supported WXR version
16
+ var $id; // WXR attachment ID
17
+ var $file;
18
+
19
+ var $import_data;
20
+ // information to import from WXR file
21
+ var $version;
22
+ var $authors = array();
23
+ var $posts = array();
24
+ var $terms = array();
25
+ var $time_slots = array();
26
+ var $categories = array();
27
+ var $tags = array();
28
+ var $base_url = '';
29
+
30
+ var $processed_authors = array();
31
+ var $author_mapping = array();
32
+ var $processed_terms = array();
33
+ var $processed_posts = array();
34
+ var $post_orphans = array();
35
+ var $processed_menu_items = array();
36
+ var $menu_item_orphans = array();
37
+ var $missing_menu_items = array();
38
+
39
+ var $fetch_attachments = false;
40
+ var $url_remap = array();
41
+ var $featured_images = array();
42
+
43
+ public static function get_instance() {
44
+ if (null === self::$instance) {
45
+ self::$instance = new self();
46
+ }
47
+ return self::$instance;
48
+ }
49
+
50
+ function __construct() {
51
+ parent::__construct();
52
+ $this->table = Events::get_instance()->table_name;
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param bool $return_me
58
+ * @param string $meta_key
59
+ *
60
+ * @return bool
61
+ */
62
+ public function mptt_filter_postmeta($return_me, $meta_key) {
63
+ if ('_edit_lock' == $meta_key)
64
+ $return_me = true;
65
+ return $return_me;
66
+ }
67
+
68
+ /**
69
+ * Exist time slot
70
+ *
71
+ * @param array $time_slot
72
+ *
73
+ * @return bool
74
+ */
75
+ public function post_time_slot_exist($time_slot = array()) {
76
+ global $wpdb;
77
+ if (empty($time_slot)) {
78
+ return false;
79
+ }
80
+ $data = $wpdb->get_results('SELECT id FROM ' . $this->table . ' WHERE column_id = "' . $time_slot['column'] . '" AND event_id = "' . $time_slot['event'] . '" AND event_start = "' . $time_slot['event_start'] . '" AND event_end = "' . $time_slot['event_end'] . '"');
81
+ return empty($data) ? false : true;
82
+ }
83
+
84
+ public function header() {
85
+ View::get_instance()->render_html('../admin/import/header');
86
+ }
87
+
88
+ public function footer() {
89
+ View::get_instance()->render_html('../admin/import/footer');
90
+ }
91
+
92
+ public function greet() {
93
+ View::get_instance()->render_html('../admin/import/greet');
94
+ }
95
+
96
+ /**
97
+ * Decide whether or not the importer is allowed to create users.
98
+ * Default is true, can be filtered via import_allow_create_users
99
+ *
100
+ * @return bool True if creating users is allowed
101
+ */
102
+ public function allow_create_users() {
103
+ return apply_filters('import_allow_create_users', true);
104
+ }
105
+
106
+ /**
107
+ * Decide whether or not the importer should attempt to download attachment files.
108
+ * Default is true, can be filtered via import_allow_fetch_attachments. The choice
109
+ * made at the import options screen must also be true, false here hides that checkbox.
110
+ *
111
+ * @return bool True if downloading attachments is allowed
112
+ */
113
+ public function allow_fetch_attachments() {
114
+ return apply_filters('import_allow_fetch_attachments', true);
115
+ }
116
+
117
+ public function import_options() {
118
+ $j = 0;
119
+ ?>
120
+ <form action="<?php echo admin_url('admin.php?import=mptt-importer&amp;step=2'); ?>" method="post">
121
+ <?php wp_nonce_field('mptt-importer'); ?>
122
+ <input type="hidden" name="import_id" value="<?php echo $this->id; ?>"/>
123
+
124
+ <?php if (!empty($this->authors)) : ?>
125
+ <h3><?php _e('Assign Authors', 'mp-timetable'); ?></h3>
126
+ <p><?php _e('To make it easier for you to edit and save the imported content, you may want to reassign the author of the imported item to an existing user of this site. For example, you may want to import all the entries as <code>admin</code>s entries.', 'mp-timetable'); ?></p>
127
+ <?php if ($this->allow_create_users()) : ?>
128
+ <p><?php printf(__('If a new user is created by WordPress, a new password will be randomly generated and the new user&#8217;s role will be set as %s. Manually changing the new user&#8217;s details will be necessary.', 'mp-timetable'), esc_html(get_option('default_role'))); ?></p>
129
+ <?php endif; ?>
130
+ <ol id="authors">
131
+ <?php foreach ($this->authors as $author) : ?>
132
+ <li><?php $this->author_select($j++, $author); ?></li>
133
+ <?php endforeach; ?>
134
+ </ol>
135
+ <?php endif; ?>
136
+
137
+ <?php if ($this->allow_fetch_attachments()) : ?>
138
+ <h3><?php _e('Import Attachments', 'mp-timetable'); ?></h3>
139
+ <p>
140
+ <input type="checkbox" value="1" name="fetch_attachments" id="import-attachments"/>
141
+ <label for="import-attachments"><?php _e('Download and import file attachments', 'mp-timetable'); ?></label>
142
+ </p>
143
+ <?php endif; ?>
144
+
145
+ <p class="submit"><input type="submit" class="button" value="<?php esc_attr_e('Submit', 'mp-timetable'); ?>"/></p>
146
+ </form>
147
+ <?php
148
+ }
149
+
150
+ /**
151
+ * Display import options for an individual author. That is, either create
152
+ * a new user based on import info or map to an existing user
153
+ *
154
+ * @param int $n Index for each author in the form
155
+ * @param array $author Author information, e.g. login, display name, email
156
+ */
157
+ function author_select($n, $author) {
158
+ _e('Import author:', 'mp-timetable');
159
+ echo ' <strong>' . esc_html($author['author_display_name']);
160
+ if ($this->version != '1.0') echo ' (' . esc_html($author['author_login']) . ')';
161
+ echo '</strong><br />';
162
+
163
+ if ($this->version != '1.0')
164
+ echo '<div style="margin-left:18px">';
165
+
166
+ $create_users = $this->allow_create_users();
167
+ if ($create_users) {
168
+ if ($this->version != '1.0') {
169
+ _e('or create new user with login name:', 'mp-timetable');
170
+ $value = '';
171
+ } else {
172
+ _e('as a new user:', 'mp-timetable');
173
+ $value = esc_attr(sanitize_user($author['author_login'], true));
174
+ }
175
+
176
+ echo ' <input type="text" name="user_new[' . $n . ']" value="' . $value . '" /><br />';
177
+ }
178
+
179
+ if (!$create_users && $this->version == '1.0')
180
+ _e('assign posts to an existing user:', 'mp-timetable');
181
+ else
182
+ _e('or assign posts to an existing user:', 'mp-timetable');
183
+ wp_dropdown_users(array('name' => "user_map[$n]", 'multi' => true, 'show_option_all' => __('- Select -', 'mp-timetable')));
184
+ echo '<input type="hidden" name="imported_authors[' . $n . ']" value="' . esc_attr($author['author_login']) . '" />';
185
+
186
+ if ($this->version != '1.0')
187
+ echo '</div>';
188
+ }
189
+
190
+ /**
191
+ * Import
192
+ */
193
+ public function import() {
194
+ $this->header();
195
+ $step = empty($_GET['step']) ? 0 : (int)$_GET['step'];
196
+ switch ($step) {
197
+ case 0:
198
+ $this->greet();
199
+ break;
200
+ case 1:
201
+ check_admin_referer('import-upload');
202
+ if ($this->handle_upload()) {
203
+ $this->import_options();
204
+ }
205
+ break;
206
+ case 2:
207
+ if (!empty($_POST['import_id'])) {
208
+ check_admin_referer('mptt-importer');
209
+ $this->fetch_attachments = (!empty($_POST['fetch_attachments']) && $this->allow_fetch_attachments());
210
+ $this->id = (int)$_POST['import_id'];
211
+ $file = get_attached_file($this->id);
212
+ } else {
213
+ check_admin_referer('import-upload');
214
+ $this->handle_upload();
215
+ $this->fetch_attachments = $this->allow_fetch_attachments();
216
+ $file = $this->file;
217
+ }
218
+ set_time_limit(0);
219
+ add_filter('import_post_meta_key', array($this, 'is_valid_meta_key'));
220
+ add_filter('http_request_timeout', array(&$this, 'bump_request_timeout'));
221
+
222
+ $this->get_author_mapping();
223
+
224
+ $this->process_start($file);
225
+ $this->process_end();
226
+ break;
227
+ }
228
+ $this->footer();
229
+ }
230
+
231
+ /**
232
+ * Import start
233
+ *
234
+ * @param $file
235
+ */
236
+ public function process_start($file) {
237
+ $this->import_data = $this->parse($file);
238
+ $this->get_authors_from_import();
239
+ $this->version = $this->import_data['version'];
240
+ $this->posts = $this->import_data['posts'];
241
+ $this->terms = $this->import_data['terms'];
242
+ $this->time_slots = $this->import_data['time_slots'];
243
+
244
+ wp_suspend_cache_invalidation(true);
245
+ $this->process_terms();
246
+ $this->process_posts();
247
+ $this->process_time_slot();
248
+ wp_suspend_cache_invalidation(false);
249
+ $this->backfill_parents();
250
+ $this->backfill_attachment_urls();
251
+ $this->remap_featured_images();
252
+ wp_defer_term_counting(true);
253
+ wp_defer_comment_counting(true);
254
+
255
+ do_action('import_start');
256
+ }
257
+
258
+ public function process_end() {
259
+ wp_import_cleanup($this->id);
260
+
261
+ wp_cache_flush();
262
+ foreach (get_taxonomies() as $tax) {
263
+ delete_option("{$tax}_children");
264
+ _get_term_hierarchy($tax);
265
+ }
266
+ wp_defer_term_counting(false);
267
+ wp_defer_comment_counting(false);
268
+
269
+ echo '<p>' . __('All done.', 'mp-timetable') . ' <a href="' . admin_url() . '">' . __('Have fun!', 'mp-timetable') . '</a>' . '</p>';
270
+ echo '<p>' . __('Remember to update the passwords and roles of imported users.', 'mp-timetable') . '</p>';
271
+
272
+ do_action('import_end');
273
+ }
274
+
275
+ /**
276
+ * If fetching attachments is enabled then attempt to create a new attachment
277
+ *
278
+ * @param array $post Attachment post details from WXR
279
+ * @param string $url URL to fetch attachment from
280
+ *
281
+ * @return int|/WP_Error Post ID on success, WP_Error otherwise
282
+ */
283
+ public function process_attachment($post, $url) {
284
+ if (!$this->fetch_attachments)
285
+ return new \WP_Error('attachment_processing_error',
286
+ __('Fetching attachments is not enabled', 'mp-timetable'));
287
+
288
+ // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
289
+ if (preg_match('|^/[\w\W]+$|', $url))
290
+ $url = rtrim($this->base_url, '/') . $url;
291
+
292
+ $upload = $this->fetch_remote_file($url, $post);
293
+ if (is_wp_error($upload))
294
+ return $upload;
295
+
296
+ if ($info = wp_check_filetype($upload['file']))
297
+ $post['post_mime_type'] = $info['type'];
298
+ else
299
+ return new \WP_Error('attachment_processing_error', __('Invalid file type', 'mp-timetable'));
300
+
301
+ $post['guid'] = $upload['url'];
302
+
303
+ // as per wp-admin/includes/upload.php
304
+ $post_id = wp_insert_attachment($post, $upload['file']);
305
+ wp_update_attachment_metadata($post_id, wp_generate_attachment_metadata($post_id, $upload['file']));
306
+
307
+ // remap resized image URLs, works by stripping the extension and remapping the URL stub.
308
+ if (preg_match('!^image/!', $info['type'])) {
309
+ $parts = pathinfo($url);
310
+ $name = basename($parts['basename'], ".{$parts['extension']}"); // PATHINFO_FILENAME in PHP 5.2
311
+
312
+ $parts_new = pathinfo($upload['url']);
313
+ $name_new = basename($parts_new['basename'], ".{$parts_new['extension']}");
314
+
315
+ $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
316
+ }
317
+
318
+ return $post_id;
319
+ }
320
+
321
+ /**
322
+ * Get file by URL and write upload
323
+ *
324
+ * @param $url
325
+ * @param $upload
326
+ * @param int $redirection
327
+ *
328
+ * @return bool
329
+ */
330
+ private function write_file($url, $upload, $redirection = 5) {
331
+ $options = array();
332
+ $options['redirection'] = $redirection;
333
+
334
+ if (false == $upload['file'])
335
+ $options['method'] = 'HEAD';
336
+ else
337
+ $options['method'] = 'GET';
338
+
339
+ $response = wp_safe_remote_request($url, $options);
340
+
341
+ if (false == $upload['file']) {
342
+ return false;
343
+ }
344
+ // GET request - write it to the supplied filename
345
+ $out_fp = fopen($upload['file'], 'w');
346
+ if (!$out_fp) {
347
+ return false;
348
+ }
349
+
350
+ fwrite($out_fp, wp_remote_retrieve_body($response));
351
+ fclose($out_fp);
352
+ clearstatcache();
353
+ return true;
354
+ }
355
+
356
+ /**
357
+ * Attempt to download a remote file attachment
358
+ *
359
+ * @param string $url URL of item to fetch
360
+ * @param array $post Attachment details
361
+ *
362
+ * @return array|\WP_Error Local file location details on success, WP_Error otherwise
363
+ */
364
+ public function fetch_remote_file($url, $post) {
365
+ // extract the file name and extension from the url
366
+ $file_name = basename($url);
367
+
368
+ // get placeholder file in the upload dir with a unique, sanitized filename
369
+ $upload = wp_upload_bits($file_name, 0, '', $post['upload_date']);
370
+ if ($upload['error'])
371
+ return new \WP_Error('upload_dir_error', $upload['error']);
372
+
373
+ // fetch the remote url and write it to the placeholder file
374
+ $response_data = wp_safe_remote_head($url);
375
+ $headers = wp_remote_retrieve_headers($response_data);
376
+ $headers['response'] = wp_remote_retrieve_response_code($response_data);
377
+ if (!$headers) {
378
+ @unlink($upload['file']);
379
+ return new \WP_Error('import_file_error', __('Remote server did not respond', 'mp-timetable'));
380
+ }
381
+
382
+ // make sure the fetch was successful
383
+ if ($headers['response'] != '200') {
384
+ @unlink($upload['file']);
385
+ return new \WP_Error('import_file_error', sprintf(__('Remote server returned error response %1$d %2$s', 'mp-timetable'), esc_html($headers['response']), get_status_header_desc($headers['response'])));
386
+ }
387
+
388
+ $this->write_file($url, $upload);
389
+
390
+ $filesize = filesize($upload['file']);
391
+
392
+ if (isset($headers['content-length']) && $filesize != $headers['content-length']) {
393
+ @unlink($upload['file']);
394
+ return new \WP_Error('import_file_error', __('Remote file is incorrect size', 'mp-timetable'));
395
+ }
396
+
397
+ if (0 == $filesize) {
398
+ @unlink($upload['file']);
399
+ return new \WP_Error('import_file_error', __('Zero size file downloaded', 'mp-timetable'));
400
+ }
401
+
402
+ $max_size = (int)$this->max_attachment_size();
403
+ if (!empty($max_size) && $filesize > $max_size) {
404
+ @unlink($upload['file']);
405
+ return new \WP_Error('import_file_error', sprintf(__('Remote file is too large, limit is %s', 'mp-timetable'), size_format($max_size)));
406
+ }
407
+
408
+ // keep track of the old and new urls so we can substitute them later
409
+ $this->url_remap[$url] = $upload['url'];
410
+ $this->url_remap[$post['guid']] = $upload['url']; // r13735, really needed?
411
+ // keep track of the destination if the remote url is redirected somewhere else
412
+ if (isset($headers['x-final-location']) && $headers['x-final-location'] != $url)
413
+ $this->url_remap[$headers['x-final-location']] = $upload['url'];
414
+
415
+ return $upload;
416
+ }
417
+
418
+ public function handle_upload() {
419
+ $file = wp_import_handle_upload();
420
+
421
+ if (isset($file['error'])) {
422
+ echo '<p><strong>' . __('Sorry, there has been an error.', 'mp-timetable') . '</strong><br />';
423
+ echo esc_html($file['error']) . '</p>';
424
+ return false;
425
+ } else if (!file_exists($file['file'])) {
426
+ echo '<p><strong>' . __('Sorry, there has been an error.', 'mp-timetable') . '</strong><br />';
427
+ printf(__('The export file could not be found at <code>%s</code>. It is likely that this was caused by a permissions problem.', 'mp-timetable'), esc_html($file['file']));
428
+ echo '</p>';
429
+ return false;
430
+ }
431
+ $this->file = $file['file'];
432
+ $this->id = (int)$file['id'];
433
+ $this->import_data = $this->parse($file['file']);
434
+
435
+ if (is_wp_error($this->import_data)) {
436
+ echo '<p><strong>' . __('Sorry, there has been an error.', 'mp-timetable') . '</strong><br />';
437
+ echo esc_html($this->import_data->get_error_message()) . '</p>';
438
+ return false;
439
+ }
440
+
441
+ $this->version = $this->import_data['version'];
442
+ if ($this->version > $this->max_wxr_version) {
443
+ echo '<div class="error"><p><strong>';
444
+ printf(__('This WXR file (version %s) may not be supported by this version of the importer. Please consider updating.', 'mp-timetable'), esc_html($this->import_data['version']));
445
+ echo '</strong></p></div>';
446
+ }
447
+ $this->get_authors_from_import();
448
+ return true;
449
+ }
450
+
451
+ /**
452
+ * Parse a WXR file
453
+ *
454
+ * @param string $file Path to WXR file for parsing
455
+ *
456
+ * @return array Information gathered from the WXR file
457
+ */
458
+ function parse($file) {
459
+ $parser = new libs\WXR_Parser();
460
+ return $parser->parse($file);
461
+ }
462
+
463
+ /**
464
+ * Decide if the given meta key maps to information we will want to import
465
+ *
466
+ * @param string $key The meta key to check
467
+ *
468
+ * @return string|bool The key if we do want to import, false if not
469
+ */
470
+ function is_valid_meta_key($key) {
471
+ // skip attachment metadata since we'll regenerate it from scratch
472
+ // skip _edit_lock as not relevant for import
473
+ if (in_array($key, array('_wp_attached_file', '_wp_attachment_metadata', '_edit_lock')))
474
+ return false;
475
+ return $key;
476
+ }
477
+
478
+ /**
479
+ * Added to http_request_timeout filter to force timeout at 60 seconds during import
480
+ * @return int 60
481
+ */
482
+ function bump_request_timeout() {
483
+ return 60;
484
+ }
485
+
486
+ /**
487
+ * Retrieve authors from parsed WXR data
488
+ *
489
+ * Uses the provided author information from WXR 1.1 files
490
+ * or extracts info from each post for WXR 1.0 files
491
+ *
492
+ */
493
+ function get_authors_from_import() {
494
+ if (!empty($this->import_data['authors'])) {
495
+ $this->authors = $this->import_data['authors'];
496
+ // no author information, grab it from the posts
497
+ } else {
498
+ foreach ($this->import_data['posts'] as $post) {
499
+ $login = sanitize_user($post['post_author'], true);
500
+ if (empty($login)) {
501
+ printf(__('Failed to import author %s. Their posts will be attributed to the current user.', 'mp-timetable'), esc_html($post['post_author']));
502
+ echo '<br />';
503
+ continue;
504
+ }
505
+
506
+ if (!isset($this->authors[$login]))
507
+ $this->authors[$login] = array(
508
+ 'author_login' => $login,
509
+ 'author_display_name' => $post['post_author']
510
+ );
511
+ }
512
+ }
513
+ }
514
+
515
+ /**
516
+ * Map old author logins to local user IDs based on decisions made
517
+ * in import options form. Can map to an existing user, create a new user
518
+ * or falls back to the current user in case of error with either of the previous
519
+ */
520
+ function get_author_mapping() {
521
+ if ( ! isset( $_POST['imported_authors'] ) )
522
+ return;
523
+
524
+ $create_users = $this->allow_create_users();
525
+
526
+ foreach ( (array) $_POST['imported_authors'] as $i => $old_login ) {
527
+ // Multisite adds strtolower to sanitize_user. Need to sanitize here to stop breakage in process_posts.
528
+ $santized_old_login = sanitize_user( $old_login, true );
529
+ $old_id = isset( $this->authors[$old_login]['author_id'] ) ? intval($this->authors[$old_login]['author_id']) : false;
530
+
531
+ if ( ! empty( $_POST['user_map'][$i] ) ) {
532
+ $user = get_userdata( intval($_POST['user_map'][$i]) );
533
+ if ( isset( $user->ID ) ) {
534
+ if ( $old_id )
535
+ $this->processed_authors[$old_id] = $user->ID;
536
+ $this->author_mapping[$santized_old_login] = $user->ID;
537
+ }
538
+ } else if ( $create_users ) {
539
+ if ( ! empty($_POST['user_new'][$i]) ) {
540
+ $user_id = wp_create_user( $_POST['user_new'][$i], wp_generate_password() );
541
+ } else if ( $this->version != '1.0' ) {
542
+ $user_data = array(
543
+ 'user_login' => $old_login,
544
+ 'user_pass' => wp_generate_password(),
545
+ 'user_email' => isset( $this->authors[$old_login]['author_email'] ) ? $this->authors[$old_login]['author_email'] : '',
546
+ 'display_name' => $this->authors[$old_login]['author_display_name'],
547
+ 'first_name' => isset( $this->authors[$old_login]['author_first_name'] ) ? $this->authors[$old_login]['author_first_name'] : '',
548
+ 'last_name' => isset( $this->authors[$old_login]['author_last_name'] ) ? $this->authors[$old_login]['author_last_name'] : '',
549
+ );
550
+ $user_id = wp_insert_user( $user_data );
551
+ }
552
+
553
+ if ( ! is_wp_error( $user_id ) ) {
554
+ if ( $old_id )
555
+ $this->processed_authors[$old_id] = $user_id;
556
+ $this->author_mapping[$santized_old_login] = $user_id;
557
+ } else {
558
+ printf( __( 'Failed to create new user for %s. Their posts will be attributed to the current user.', 'mp-timetable' ), esc_html($this->authors[$old_login]['author_display_name']) );
559
+ if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG )
560
+ echo ' ' . $user_id->get_error_message();
561
+ echo '<br />';
562
+ }
563
+ }
564
+
565
+ // failsafe: if the user_id was invalid, default to the current user
566
+ if ( ! isset( $this->author_mapping[$santized_old_login] ) ) {
567
+ if ( $old_id )
568
+ $this->processed_authors[$old_id] = (int) get_current_user_id();
569
+ $this->author_mapping[$santized_old_login] = (int) get_current_user_id();
570
+ }
571
+ }
572
+ }
573
+
574
+ /**
575
+ * Create new terms based on import information
576
+ *
577
+ * Doesn't create a term its slug already exists
578
+ */
579
+ function process_terms() {
580
+ $this->terms = apply_filters('wp_import_terms', $this->terms);
581
+
582
+ if (empty($this->terms))
583
+ return;
584
+
585
+ foreach ($this->terms as $term) {
586
+ // if the term already exists in the correct taxonomy leave it alone
587
+ $term_id = term_exists($term['slug'], $term['term_taxonomy']);
588
+ if ($term_id) {
589
+ if (is_array($term_id)) $term_id = $term_id['term_id'];
590
+ if (isset($term['term_id']))
591
+ $this->processed_terms[intval($term['term_id'])] = (int)$term_id;
592
+ continue;
593
+ }
594
+
595
+ if (empty($term['term_parent'])) {
596
+ $parent = 0;
597
+ } else {
598
+ $parent = term_exists($term['term_parent'], $term['term_taxonomy']);
599
+ if (is_array($parent)) $parent = $parent['term_id'];
600
+ }
601
+ $description = isset($term['term_description']) ? $term['term_description'] : '';
602
+ $termarr = array('slug' => $term['slug'], 'description' => $description, 'parent' => intval($parent));
603
+
604
+ $id = wp_insert_term($term['term_name'], $term['term_taxonomy'], $termarr);
605
+ if (!is_wp_error($id)) {
606
+ if (isset($term['term_id']))
607
+ $this->processed_terms[intval($term['term_id'])] = $id['term_id'];
608
+ } else {
609
+ printf(__('Failed to import %s %s', 'mp-timetable'), esc_html($term['term_taxonomy']), esc_html($term['term_name']));
610
+ if (defined('IMPORT_DEBUG') && IMPORT_DEBUG)
611
+ echo ': ' . $id->get_error_message();
612
+ echo '<br />';
613
+ continue;
614
+ }
615
+ }
616
+
617
+ unset($this->terms);
618
+ }
619
+
620
+ public function process_time_slot() {
621
+ global $wpdb;
622
+ $rows_affected = array();
623
+ $time_slots = $this->import_data['time_slots'];
624
+ if (!empty($this->import_data['time_slots'])) {
625
+ foreach ($time_slots as $time_slot) {
626
+ $exist_time_slot = $this->post_time_slot_exist($time_slot);
627
+ if (!$exist_time_slot) {
628
+ $rows_affected[] = $wpdb->insert($this->table, array(
629
+ 'column_id' => $time_slot['column'],
630
+ 'event_id' => $time_slot['event'],
631
+ 'event_start' => date('H:i:s', strtotime($time_slot['event_start'])),
632
+ 'event_end' => date('H:i:s', strtotime($time_slot['event_end'])),
633
+ 'user_id' => $time_slot['user_id'],
634
+ 'description' => $time_slot['description']
635
+ ));
636
+ }
637
+ }
638
+ }
639
+ }
640
+
641
+ /**
642
+ * Create new posts based on import information
643
+ *
644
+ * Posts marked as having a parent which doesn't exist will become top level items.
645
+ * Doesn't create a new post if: the post type doesn't exist, the given post ID
646
+ * is already noted as imported or a post with the same title and date already exists.
647
+ * Note that new/updated terms, comments and meta are imported for the last of the above.
648
+ */
649
+ public function process_posts() {
650
+ $this->posts = apply_filters('wp_import_posts', $this->posts);
651
+
652
+ foreach ($this->posts as $post) {
653
+ $post = apply_filters('wp_import_post_data_raw', $post);
654
+
655
+ if (!post_type_exists($post['post_type'])) {
656
+ printf(__('Failed to import &#8220;%s&#8221;: Invalid post type %s', 'mp-timetable'),
657
+ esc_html($post['post_title']), esc_html($post['post_type']));
658
+ echo '<br />';
659
+ do_action('wp_import_post_exists', $post);
660
+ continue;
661
+ }
662
+
663
+ if (isset($this->processed_posts[$post['post_id']]) && !empty($post['post_id']))
664
+ continue;
665
+
666
+ if ($post['status'] == 'auto-draft')
667
+ continue;
668
+
669
+ $post_type_object = get_post_type_object($post['post_type']);
670
+
671
+ $post_exists = post_exists($post['post_title'], '', $post['post_date']);
672
+ if ($post_exists && get_post_type($post_exists) == $post['post_type']) {
673
+ printf(__('%s &#8220;%s&#8221; already exists.', 'mp-timetable'), $post_type_object->labels->singular_name, esc_html($post['post_title']));
674
+ echo '<br />';
675
+ $comment_post_ID = $post_id = $post_exists;
676
+ } else {
677
+ $post_parent = (int)$post['post_parent'];
678
+ if ($post_parent) {
679
+ // if we already know the parent, map it to the new local ID
680
+ if (isset($this->processed_posts[$post_parent])) {
681
+ $post_parent = $this->processed_posts[$post_parent];
682
+ // otherwise record the parent for later
683
+ } else {
684
+ $this->post_orphans[intval($post['post_id'])] = $post_parent;
685
+ $post_parent = 0;
686
+ }
687
+ }
688
+
689
+ // map the post author
690
+ $author = sanitize_user($post['post_author'], true);
691
+ if (isset($this->author_mapping[$author]))
692
+ $author = $this->author_mapping[$author];
693
+ else
694
+ $author = (int)get_current_user_id();
695
+
696
+ $postdata = array(
697
+ 'import_id' => $post['post_id'], 'post_author' => $author, 'post_date' => $post['post_date'],
698
+ 'post_date_gmt' => $post['post_date_gmt'], 'post_content' => $post['post_content'],
699
+ 'post_excerpt' => $post['post_excerpt'], 'post_title' => $post['post_title'],
700
+ 'post_status' => $post['status'], 'post_name' => $post['post_name'],
701
+ 'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'],
702
+ 'guid' => $post['guid'], 'post_parent' => $post_parent, 'menu_order' => $post['menu_order'],
703
+ 'post_type' => $post['post_type'], 'post_password' => $post['post_password']
704
+ );
705
+
706
+ $original_post_ID = $post['post_id'];
707
+ $postdata = apply_filters('wp_import_post_data_processed', $postdata, $post);
708
+
709
+ if ('attachment' == $postdata['post_type']) {
710
+ $remote_url = !empty($post['attachment_url']) ? $post['attachment_url'] : $post['guid'];
711
+
712
+ // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
713
+ // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
714
+ $postdata['upload_date'] = $post['post_date'];
715
+ if (isset($post['postmeta'])) {
716
+ foreach ($post['postmeta'] as $meta) {
717
+ if ($meta['key'] == '_wp_attached_file') {
718
+ if (preg_match('%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches))
719
+ $postdata['upload_date'] = $matches[0];
720
+ break;
721
+ }
722
+ }
723
+ }
724
+
725
+ $comment_post_ID = $post_id = $this->process_attachment($postdata, $remote_url);
726
+ } else {
727
+ $comment_post_ID = $post_id = wp_insert_post($postdata, true);
728
+ do_action('wp_import_insert_post', $post_id, $original_post_ID, $postdata, $post);
729
+ }
730
+
731
+ if (is_wp_error($post_id)) {
732
+ printf(__('Failed to import %s &#8220;%s&#8221;', 'mp-timetable'),
733
+ $post_type_object->labels->singular_name, esc_html($post['post_title']));
734
+ if (defined('IMPORT_DEBUG') && IMPORT_DEBUG)
735
+ echo ': ' . $post_id->get_error_message();
736
+ echo '<br />';
737
+ continue;
738
+ }
739
+
740
+ if ($post['is_sticky'] == 1)
741
+ stick_post($post_id);
742
+ }
743
+
744
+ // map pre-import ID to local ID
745
+ $this->processed_posts[intval($post['post_id'])] = (int)$post_id;
746
+
747
+ if (!isset($post['terms']))
748
+ $post['terms'] = array();
749
+
750
+ $post['terms'] = apply_filters('wp_import_post_terms', $post['terms'], $post_id, $post);
751
+
752
+ // add categories, tags and other terms
753
+ if (!empty($post['terms'])) {
754
+ $terms_to_set = array();
755
+ foreach ($post['terms'] as $term) {
756
+ // back compat with WXR 1.0 map 'tag' to 'post_tag'
757
+ $taxonomy = ('tag' == $term['domain']) ? 'post_tag' : $term['domain'];
758
+ $term_exists = term_exists($term['slug'], $taxonomy);
759
+ $term_id = is_array($term_exists) ? $term_exists['term_id'] : $term_exists;
760
+ if (!$term_id) {
761
+ $t = wp_insert_term($term['name'], $taxonomy, array('slug' => $term['slug']));
762
+ if (!is_wp_error($t)) {
763
+ $term_id = $t['term_id'];
764
+ do_action('wp_import_insert_term', $t, $term, $post_id, $post);
765
+ } else {
766
+ printf(__('Failed to import %s %s', 'mp-timetable'), esc_html($taxonomy), esc_html($term['name']));
767
+ if (defined('IMPORT_DEBUG') && IMPORT_DEBUG)
768
+ echo ': ' . $t->get_error_message();
769
+ echo '<br />';
770
+ do_action('wp_import_insert_term_failed', $t, $term, $post_id, $post);
771
+ continue;
772
+ }
773
+ }
774
+ $terms_to_set[$taxonomy][] = intval($term_id);
775
+ }
776
+
777
+ foreach ($terms_to_set as $tax => $ids) {
778
+ $tt_ids = wp_set_post_terms($post_id, $ids, $tax);
779
+ do_action('wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $post);
780
+ }
781
+ unset($post['terms'], $terms_to_set);
782
+ }
783
+
784
+ if (!isset($post['comments']))
785
+ $post['comments'] = array();
786
+
787
+ $post['comments'] = apply_filters('wp_import_post_comments', $post['comments'], $post_id, $post);
788
+
789
+ // add/update comments
790
+ if (!empty($post['comments'])) {
791
+ $num_comments = 0;
792
+ $inserted_comments = array();
793
+ foreach ($post['comments'] as $comment) {
794
+ $comment_id = $comment['comment_id'];
795
+ $newcomments[$comment_id]['comment_post_ID'] = $comment_post_ID;
796
+ $newcomments[$comment_id]['comment_author'] = $comment['comment_author'];
797
+ $newcomments[$comment_id]['comment_author_email'] = $comment['comment_author_email'];
798
+ $newcomments[$comment_id]['comment_author_IP'] = $comment['comment_author_IP'];
799
+ $newcomments[$comment_id]['comment_author_url'] = $comment['comment_author_url'];
800
+ $newcomments[$comment_id]['comment_date'] = $comment['comment_date'];
801
+ $newcomments[$comment_id]['comment_date_gmt'] = $comment['comment_date_gmt'];
802
+ $newcomments[$comment_id]['comment_content'] = $comment['comment_content'];
803
+ $newcomments[$comment_id]['comment_approved'] = $comment['comment_approved'];
804
+ $newcomments[$comment_id]['comment_type'] = $comment['comment_type'];
805
+ $newcomments[$comment_id]['comment_parent'] = $comment['comment_parent'];
806
+ $newcomments[$comment_id]['commentmeta'] = isset($comment['commentmeta']) ? $comment['commentmeta'] : array();
807
+ if (isset($this->processed_authors[$comment['comment_user_id']]))
808
+ $newcomments[$comment_id]['user_id'] = $this->processed_authors[$comment['comment_user_id']];
809
+ }
810
+ ksort($newcomments);
811
+
812
+ foreach ($newcomments as $key => $comment) {
813
+ // if this is a new post we can skip the comment_exists() check
814
+ if (!$post_exists || !comment_exists($comment['comment_author'], $comment['comment_date'])) {
815
+ if (isset($inserted_comments[$comment['comment_parent']]))
816
+ $comment['comment_parent'] = $inserted_comments[$comment['comment_parent']];
817
+ $comment = wp_filter_comment($comment);
818
+ $inserted_comments[$key] = wp_insert_comment($comment);
819
+ do_action('wp_import_insert_comment', $inserted_comments[$key], $comment, $comment_post_ID, $post);
820
+
821
+ foreach ($comment['commentmeta'] as $meta) {
822
+ $value = maybe_unserialize($meta['value']);
823
+ add_comment_meta($inserted_comments[$key], $meta['key'], $value);
824
+ }
825
+
826
+ $num_comments++;
827
+ }
828
+ }
829
+ unset($newcomments, $inserted_comments, $post['comments']);
830
+ }
831
+
832
+ // change time_slot data if post ID change
833
+
834
+ if (!empty($original_post_ID)) {
835
+ if ($original_post_ID != $post_id) {
836
+ foreach ($this->import_data['time_slots'] as $key => $time_slot) {
837
+ if ($post['post_type'] == 'mp-event' && $time_slot['event'] == $original_post_ID) {
838
+ $time_slot['event'] = $post_id;
839
+ } elseif ($post['post_type'] == 'mp-column' && $time_slot['column'] == $original_post_ID) {
840
+ $time_slot['column'] = $post_id;
841
+ } else {
842
+ continue;
843
+ }
844
+ $this->import_data['time_slots'][$key] = $time_slot;
845
+ }
846
+ }
847
+ }
848
+
849
+
850
+ if (!isset($post['postmeta'])) {
851
+ $post['postmeta'] = array();
852
+ }
853
+ $post['postmeta'] = apply_filters('wp_import_post_meta', $post['postmeta'], $post_id, $post);
854
+ // add/update post meta
855
+ if (!empty($post['postmeta'])) {
856
+ foreach ($post['postmeta'] as $meta) {
857
+ $key = apply_filters('import_post_meta_key', $meta['key'], $post_id, $post);
858
+ $value = false;
859
+
860
+ if ('_edit_last' == $key) {
861
+ if (isset($this->processed_authors[intval($meta['value'])]))
862
+ $value = $this->processed_authors[intval($meta['value'])];
863
+ else
864
+ $key = false;
865
+ }
866
+
867
+ if ($key) {
868
+ // export gets meta straight from the DB so could have a serialized string
869
+ if (!$value)
870
+ $value = maybe_unserialize($meta['value']);
871
+
872
+ add_post_meta($post_id, $key, $value);
873
+ do_action('import_post_meta', $post_id, $key, $value);
874
+
875
+ // if the post has a featured image, take note of this in case of remap
876
+ if ('_thumbnail_id' == $key)
877
+ $this->featured_images[$post_id] = (int)$value;
878
+ }
879
+ }
880
+ }
881
+ }
882
+
883
+ unset($this->posts);
884
+ }
885
+
886
+ /**
887
+ * Attempt to associate posts and menu items with previously missing parents
888
+ *
889
+ * An imported post's parent may not have been imported when it was first created
890
+ * so try again. Similarly for child menu items and menu items which were missing
891
+ * the object (e.g. post) they represent in the menu
892
+ */
893
+ function backfill_parents() {
894
+ global $wpdb;
895
+
896
+ // find parents for post orphans
897
+ foreach ($this->post_orphans as $child_id => $parent_id) {
898
+ $local_child_id = $local_parent_id = false;
899
+ if (isset($this->processed_posts[$child_id]))
900
+ $local_child_id = $this->processed_posts[$child_id];
901
+ if (isset($this->processed_posts[$parent_id]))
902
+ $local_parent_id = $this->processed_posts[$parent_id];
903
+
904
+ if ($local_child_id && $local_parent_id)
905
+ $wpdb->update($wpdb->posts, array('post_parent' => $local_parent_id), array('ID' => $local_child_id), '%d', '%d');
906
+ }
907
+
908
+ // all other posts/terms are imported, retry menu items with missing associated object
909
+ $missing_menu_items = $this->missing_menu_items;
910
+ // foreach ($missing_menu_items as $item)
911
+ // $this->process_menu_item($item);
912
+
913
+ // find parents for menu item orphans
914
+ foreach ($this->menu_item_orphans as $child_id => $parent_id) {
915
+ $local_child_id = $local_parent_id = 0;
916
+ if (isset($this->processed_menu_items[$child_id]))
917
+ $local_child_id = $this->processed_menu_items[$child_id];
918
+ if (isset($this->processed_menu_items[$parent_id]))
919
+ $local_parent_id = $this->processed_menu_items[$parent_id];
920
+
921
+ if ($local_child_id && $local_parent_id)
922
+ update_post_meta($local_child_id, '_menu_item_menu_item_parent', (int)$local_parent_id);
923
+ }
924
+ }
925
+
926
+ /**
927
+ * Use stored mapping information to update old attachment URLs
928
+ */
929
+ function backfill_attachment_urls() {
930
+ global $wpdb;
931
+ $result = array();
932
+ // make sure we do the longest urls first, in case one is a substring of another
933
+ uksort($this->url_remap, array(&$this, 'cmpr_strlen'));
934
+
935
+ foreach ($this->url_remap as $from_url => $to_url) {
936
+ // remap urls in post_content
937
+ $wpdb->query($wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url));
938
+ // remap enclosure urls
939
+ $result = $wpdb->query($wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url));
940
+ }
941
+ return $result;
942
+ }
943
+
944
+ // return the difference in length between two strings
945
+ public function cmpr_strlen($a, $b) {
946
+ return strlen($b) - strlen($a);
947
+ }
948
+
949
+ /**
950
+ * Update _thumbnail_id meta to new, imported attachment IDs
951
+ */
952
+ function remap_featured_images() {
953
+ // cycle through posts that have a featured image
954
+ foreach ($this->featured_images as $post_id => $value) {
955
+ if (isset($this->processed_posts[$value])) {
956
+ $new_id = $this->processed_posts[$value];
957
+ // only update if there's a difference
958
+ if ($new_id != $value)
959
+ update_post_meta($post_id, '_thumbnail_id', $new_id);
960
+ }
961
+ }
962
+ }
963
+
964
+ function max_attachment_size() {
965
+ return apply_filters('import_attachment_size_limit', 0);
966
+ }
967
+ }
968
+
classes/models/class-settings.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\classes\models;
4
+
5
+ use mp_timetable\plugin_core\classes\Model as Model;
6
+
7
+ /**
8
+ * Model Events
9
+ */
10
+ class Settings extends Model {
11
+
12
+ protected static $instance;
13
+
14
+ /**
15
+ * Settings constructor.
16
+ */
17
+ public function __construct() {
18
+ parent::__construct();
19
+ }
20
+
21
+ /**
22
+ * Get instance
23
+ *
24
+ * @return Settings
25
+ */
26
+ public static function get_instance() {
27
+ if (null === self::$instance) {
28
+ self::$instance = new self();
29
+ }
30
+ return self::$instance;
31
+ }
32
+
33
+ /**
34
+ * Render settings
35
+ */
36
+ public function render_settings() {
37
+ $data = $this->get_settings();
38
+
39
+ $this->get_view()->render_html("settings/general", array('settings' => $data), true);
40
+ }
41
+
42
+ /**
43
+ * Get Settings
44
+ *
45
+ * @return mixed
46
+ */
47
+ public function get_settings() {
48
+
49
+ $mp_timetable_general = array(
50
+ 'theme_mode' => 'theme',
51
+ );
52
+
53
+ $settings = get_option('mp_timetable_general', $mp_timetable_general);
54
+
55
+ if ($this->is_theme_supports()) {
56
+ $settings['theme_mode'] = 'plugin';
57
+ }
58
+
59
+ return $settings;
60
+ }
61
+
62
+ /**
63
+ * Theme supports plugin mode.
64
+ *
65
+ * @return string
66
+ */
67
+ public function is_theme_supports() {
68
+
69
+ return current_theme_supports('mp-timetable');
70
+ }
71
+
72
+ /**
73
+ * Save meta data Column post type
74
+ *
75
+ */
76
+ public function save_settings() {
77
+ $saved = false;
78
+ $options = array();
79
+
80
+ if (isset($_POST['mp-timetable-save-settings']) && wp_verify_nonce($_POST['mp-timetable-save-settings'], 'mp_timetable_nonce_settings')) {
81
+ if (!empty($_POST['theme_mode'])) {
82
+ $options['theme_mode'] = $_POST['theme_mode'];
83
+ $saved = update_option('mp_timetable_general', $options);
84
+ }
85
+ }
86
+
87
+ return $saved;
88
+ }
89
+
90
+ /**
91
+ * Check whether to use single room template from plugin
92
+ *
93
+ * @return bool
94
+ */
95
+ public function is_plugin_template_mode() {
96
+ return ($this->get_template_mode() === 'plugin');
97
+ }
98
+
99
+ /**
100
+ * Retrieve template mode. Possible values: plugin, theme.
101
+ *
102
+ * @return string
103
+ */
104
+ public function get_template_mode() {
105
+ $options = $this->get_settings();
106
+
107
+ return isset($options['theme_mode']) ? $options['theme_mode'] : 'theme';
108
+ }
109
+ }
classes/modules/class-menu.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes\modules;
4
+
5
+ class Menu {
6
+
7
+ protected static $instance;
8
+
9
+ public static function get_instance() {
10
+ if (null === self::$instance) {
11
+ self::$instance = new self();
12
+ }
13
+ return self::$instance;
14
+ }
15
+
16
+ /**
17
+ * Add menu page
18
+ *
19
+ * @param $params
20
+ */
21
+ public function add_menu_page(array $params) {
22
+ add_menu_page($params['page_title'], $params['menu_title'], $params['capability'], $params['menu_slug'], $params['function'], $params['icon_url'], $params['position']);
23
+ }
24
+
25
+ /**
26
+ * Add submenu page
27
+ *
28
+ * @param $params
29
+ */
30
+ public function add_submenu_page(array $params) {
31
+ add_submenu_page($params['parent_slug'], $params['page_title'], $params['menu_title'], $params['capability'], $params['menu_slug'], $params['function']);
32
+ }
33
+ }
classes/modules/class-post.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes\modules;
4
+
5
+ use Mp_Time_Table;
6
+ use mp_timetable\plugin_core\classes\Module as Module;
7
+
8
+ class Post extends Module {
9
+
10
+ protected static $instance;
11
+
12
+ public static function get_instance() {
13
+ if (null === self::$instance) {
14
+ self::$instance = new self();
15
+ }
16
+ return self::$instance;
17
+ }
18
+
19
+ public function pre_get_posts($query) {
20
+ if ($query->is_author() && $query->is_main_query() && !is_admin()) {
21
+
22
+ $post_types = $query->get('post_type');
23
+ if ( !is_array($post_types) && !empty($post_types) ) {
24
+ $post_types = explode(',', $post_types);
25
+ }
26
+ if ( empty($post_types) )
27
+ $post_types = array('post');
28
+
29
+ $post_types[] = 'mp-event';
30
+ $query->set('post_type', $post_types);
31
+ }
32
+
33
+ return $query;
34
+ }
35
+
36
+ /**
37
+ * add meta _boxes
38
+ */
39
+ public function add_meta_boxes() {
40
+ add_meta_box('mp-event_data', __('Timeslots', 'mp-timetable'), array($this->get('events'), 'render_event_data'), 'mp-event', 'normal', 'high', array('post_type' => 'mp-event'));
41
+ add_meta_box('mp_event_options', __('Settings', 'mp-timetable'), array($this->get('events'), 'render_event_options'), 'mp-event', 'normal', 'high', array('post_type' => 'mp-event'));
42
+ add_meta_box('mp-columns', __('Column Type', 'mp-timetable'), array($this->get('column'), 'render_column_options'), 'mp-column', 'normal', 'high', array('post_type' => 'mp-column'));
43
+ }
44
+
45
+ /**
46
+ * Save custom_post
47
+ *
48
+ * @param $post_id
49
+ * @param $post
50
+ */
51
+ public function save_custom_post($post_id, $post) {
52
+ $request = $_REQUEST;
53
+ if (!empty($request[Mp_Time_Table::get_plugin_name() . '_noncename'])) {
54
+ $post_type = $request['post_type'];
55
+ if (!wp_verify_nonce($request[Mp_Time_Table::get_plugin_name() . '_noncename'], Mp_Time_Table::get_plugin_path())) {
56
+ return $post->ID;
57
+ }
58
+
59
+ // Is the user allowed to edit the post or page?
60
+ if (!current_user_can('edit_post', $post->ID)) {
61
+ return $post->ID;
62
+ }
63
+ // verify if this is an auto save routine. If it is our form has not been submitted, so we dont want to do anything
64
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
65
+ return $post->ID;
66
+ }
67
+
68
+ //Save post by post_type
69
+ switch ($post_type) {
70
+ case 'mp-event':
71
+ $this->get('events')->save_event_data(array('post' => $post,
72
+ 'event_data' => (!empty($request['event_data'])) ? $request['event_data'] : null,
73
+ 'event_meta' => (!empty($request['event_meta'])) ? $request['event_meta'] : null));
74
+ break;
75
+ case 'mp-column':
76
+ $this->get('column')->save_column_data(array('post' => $post, 'data' => $request['column']));
77
+ break;
78
+ default:
79
+ break;
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Before delete custom post
86
+ *
87
+ * @param $post_id
88
+ * @param $post
89
+ */
90
+ public function before_delete_custom_post($post_id) {
91
+ global $post_type;
92
+ if ($post_type === 'mp-column') {
93
+ $this->get('column')->before_delete_column($post_id);
94
+ } elseif ($post_type === 'mp-event') {
95
+ $this->get('events')->before_delete_event($post_id);
96
+ }
97
+
98
+ return;
99
+ }
100
+ }
classes/modules/class-taxonomy.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace mp_timetable\plugin_core\classes\modules;
4
+
5
+ use mp_timetable\plugin_core\classes\Module;
6
+ use mp_timetable\plugin_core\classes\View;
7
+
8
+ class Taxonomy extends Module {
9
+
10
+ protected static $instance;
11
+
12
+ public static function get_instance() {
13
+ if (null === self::$instance) {
14
+ self::$instance = new self();
15
+ }
16
+ return self::$instance;
17
+ }
18
+
19
+
20
+ /**
21
+ * Render html for filter taxonomy link
22
+ *
23
+ * @param $post
24
+ * @param $tax_name
25
+ *
26
+ * @return string
27
+ */
28
+ public function get_the_term_filter_list($post, $tax_name) {
29
+ $taxonomies = wp_get_post_terms($post->ID, $tax_name);
30
+ $taxonomies_html = "";
31
+ $last_key = key(array_slice($taxonomies, -1, 1, TRUE));
32
+
33
+ foreach ($taxonomies as $key => $tax) {
34
+ $data["wp"] = $tax;
35
+ $data["filter_link"] = admin_url('edit.php?post_type=' . $post->post_type . '&' . $tax->taxonomy . '=' . $tax->slug);
36
+ $taxonomies_html .= View::get_instance()->render_html("taxonomies/taxonomy-link", $data, false);
37
+ $taxonomies_html .= ($last_key != $key) ? ', ' : '';
38
+ }
39
+ return (!empty($taxonomies_html)) ? $taxonomies_html : "—";
40
+ }
41
+
42
+ }
classes/widgets/class-mp-timetable-widget.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace timetable\classes\widgets;
4
+
5
+ use mp_timetable\classes\models\Column;
6
+ use mp_timetable\classes\models\Events;
7
+ use mp_timetable\classes\models\Settings;
8
+ use mp_timetable\plugin_core\classes\Core;
9
+ use mp_timetable\plugin_core\classes\View;
10
+
11
+ class Timetable_widget extends \WP_Widget {
12
+
13
+ /**
14
+ * Widget constructor.
15
+ */
16
+ public function __construct() {
17
+
18
+ $classname = Settings::get_instance()->is_plugin_template_mode() ? 'mptt-container' : 'widget_recent_entries';
19
+
20
+ $widget_ops = array(
21
+ 'classname' => $classname,
22
+ 'description' => __( 'Display upcoming events.', 'mp-timetable' )
23
+ );
24
+ parent::__construct( 'mp-timetable', __( 'Timetable Events', 'mp-timetable' ), $widget_ops );
25
+ add_action( 'save_post', array( &$this, 'flush_widget_cache' ) );
26
+ add_action( 'deleted_post', array( &$this, 'flush_widget_cache' ) );
27
+ add_action( 'switch_theme', array( &$this, 'flush_widget_cache' ) );
28
+ }
29
+
30
+ public function form( $instance ) {
31
+ $instance = shortcode_atts( array(
32
+ 'title' => '',
33
+ 'limit' => '3',
34
+ 'view_settings' => '',
35
+ 'mp_categories' => '',
36
+ 'next_days' => '1',
37
+ 'time_settings' => '',
38
+ 'custom_url' => '',
39
+ 'disable_url' => '',
40
+ 'background_color' => '',
41
+ 'hover_background_color' => '',
42
+ 'text_color' => '',
43
+ 'hover_text_color' => '',
44
+ 'item_border_color' => '',
45
+ 'hover_item_border_color' => '',
46
+ ), $instance );
47
+
48
+ $data[ 'columns' ] = Column::get_instance()->get_all_column();
49
+ $data[ 'events' ] = Events::get_instance()->get_all_events();
50
+ $data[ 'categories' ] = get_terms( 'mp-event_category', 'orderby=count&hide_empty=0' );;
51
+ $data[ 'localtime' ] = date( get_option( 'time_format' ), current_time( 'timestamp', 0 ) );
52
+ $data[ 'utc_time' ] = date( get_option( 'time_format' ), current_time( 'timestamp', 1 ) );
53
+ View::get_instance()->render_html( 'widgets/gallery-list', array( 'widget_object' => $this, 'data' => $data, 'instance' => $instance ), true );
54
+ }
55
+
56
+ /**
57
+ * Update widget
58
+ *
59
+ * @param array $new_instance
60
+ * @param array $old_instance
61
+ *
62
+ * @return array
63
+ */
64
+ public function update( $new_instance, $old_instance ) {
65
+
66
+ $instance = $old_instance;
67
+ $instance[ 'title' ] = strip_tags( $new_instance[ 'title' ] );
68
+ $instance[ 'limit' ] = strip_tags( $new_instance[ 'limit' ] );
69
+ $instance[ 'mp_categories' ] = empty( $new_instance[ 'mp_categories' ] ) ? "" : $new_instance[ 'mp_categories' ];
70
+ $instance[ 'view_settings' ] = strip_tags( $new_instance[ 'view_settings' ] );
71
+ $instance[ 'next_days' ] = strip_tags( $new_instance[ 'next_days' ] );
72
+ $instance[ 'time_settings' ] = empty( $new_instance[ 'time_settings' ] ) ? "" : strip_tags( $new_instance[ 'time_settings' ] );
73
+ $instance[ 'custom_url' ] = strip_tags( $new_instance[ 'custom_url' ] );
74
+ $instance[ 'disable_url' ] = strip_tags( $new_instance[ 'disable_url' ] );
75
+ $instance[ 'background_color' ] = strip_tags( $new_instance[ 'background_color' ] );
76
+ $instance[ 'hover_background_color' ] = strip_tags( $new_instance[ 'hover_background_color' ] );
77
+ $instance[ 'text_color' ] = strip_tags( $new_instance[ 'text_color' ] );
78
+ $instance[ 'hover_text_color' ] = strip_tags( $new_instance[ 'hover_text_color' ] );
79
+ $instance[ 'item_border_color' ] = strip_tags( $new_instance[ 'item_border_color' ] );
80
+ $instance[ 'hover_item_border_color' ] = strip_tags( $new_instance[ 'hover_item_border_color' ] );
81
+
82
+ return $instance;
83
+ }
84
+
85
+ /**
86
+ * Flush widget cache.
87
+ *
88
+ * @since Twenty Eleven 1.0
89
+ */
90
+ function flush_widget_cache() {
91
+ wp_cache_delete( 'mp-timetable', 'widget' );
92
+ }
93
+
94
+ /**
95
+ * Display widget
96
+ *
97
+ * @param array $args
98
+ * @param array $instance
99
+ */
100
+ public function widget( $args, $instance ) {
101
+ $cache = wp_cache_get( 'mp-timetable', 'widget' );
102
+
103
+ if ( ! is_array( $cache ) ) {
104
+ $cache = array();
105
+ }
106
+
107
+ if ( ! isset( $args[ 'widget_id' ] ) ) {
108
+ $args[ 'widget_id' ] = null;
109
+ }
110
+
111
+ if ( isset( $cache[ $args[ 'widget_id' ] ] ) ) {
112
+ echo $cache[ $args[ 'widget_id' ] ];
113
+
114
+ return;
115
+ }
116
+
117
+ ob_start();
118
+ $data[ 'args' ] = $args;
119
+ $data[ 'instance' ] = mptt_widget_settings( $instance );
120
+ $data[ 'events' ] = Events::get_instance()->get_widget_events( $data[ 'instance' ] );
121
+ if ( Settings::get_instance()->is_plugin_template_mode() ) {
122
+ Core::get_instance()->add_plugin_js( 'widget' );
123
+ View::get_instance()->get_template( "widgets/widget-view", $data );
124
+ } else {
125
+ View::get_instance()->get_template( "theme/widget-upcoming-view", $data );
126
+ }
127
+
128
+ $cache[ $args[ 'widget_id' ] ] = ob_get_flush();
129
+ wp_cache_set( 'mp-timetable', $cache, 'widget' );
130
+ }
131
+ }
132
+
languages/mp-timetable-fa_IR.mo ADDED
Binary file
languages/mp-timetable-fa_IR.po ADDED
@@ -0,0 +1,962 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Timetable and Event Schedule Plugin\n"
4
+ "POT-Creation-Date: 2018-05-18 09:58+0430\n"
5
+ "PO-Revision-Date: \n"
6
+ "Last-Translator: \n"
7
+ "Language-Team: Alireza Rastegar<alireza.mine@gmail.com>\n"
8
+ "Language: fa_IR\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=1; plural=0;\n"
13
+ "X-Generator: Poedit 2.0.1\n"
14
+ "X-Poedit-SourceCharset: UTF-8\n"
15
+ "X-Poedit-KeywordsList: esc_attr__;esc_html__;esc_attr_e;__;_e;_n;_x\n"
16
+ "X-Poedit-Basepath: ..\n"
17
+ "X-Poedit-SearchPath-0: .\n"
18
+ "X-Poedit-SearchPathExcluded-0: classes/libs\n"
19
+ "X-Poedit-SearchPathExcluded-1: media\n"
20
+
21
+ #: admin/import/export.php:1 admin/import/export.php:5
22
+ msgid "Export"
23
+ msgstr "برونریزی"
24
+
25
+ #: admin/import/greet.php:2
26
+ msgid "Choose a WXR (.xml) file to upload, then click Upload file and import."
27
+ msgstr ""
28
+ "یک فایل WXR (.xml) را برای آپلود انتخاب کنید، سپس روی آپلود فایل و وارد کردن "
29
+ "کلیک کنید."
30
+
31
+ #: admin/import/header.php:2
32
+ msgid "Import / Export Timetable Plugin Data"
33
+ msgstr "درون ریزی / برون ریزی داده افزونه جدول زمانی"
34
+
35
+ #: admin/import/import.php:1
36
+ msgid "Import"
37
+ msgstr "درون ریزی"
38
+
39
+ #: classes/class-core.php:317 classes/class-core.php:329
40
+ #: classes/class-core.php:333 classes/class-shortcode.php:261
41
+ msgid "Event categories"
42
+ msgstr "دسته های رویداد"
43
+
44
+ #: classes/class-core.php:318
45
+ msgid "Event category"
46
+ msgstr "دسته بندی رویداد"
47
+
48
+ #: classes/class-core.php:319 classes/class-core.php:320
49
+ msgid "Add New Event category"
50
+ msgstr "افزودن دسته بندی رویداد"
51
+
52
+ #: classes/class-core.php:321
53
+ msgid "Edit Event category"
54
+ msgstr "ویرایش دسته بندی رویداد"
55
+
56
+ #: classes/class-core.php:322
57
+ msgid "New Event category"
58
+ msgstr "دسته رویداد جدید"
59
+
60
+ #: classes/class-core.php:323
61
+ msgid "All Event categories"
62
+ msgstr "همه دسته های رویداد"
63
+
64
+ #: classes/class-core.php:324
65
+ msgid "View Event category"
66
+ msgstr "نمایش دسته رویداد"
67
+
68
+ #: classes/class-core.php:325
69
+ msgid "Search Event category"
70
+ msgstr "جستجوی دسته رویداد"
71
+
72
+ #: classes/class-core.php:326
73
+ msgid "No Event categories found"
74
+ msgstr "دسته رویدادی پیدا نشد"
75
+
76
+ #: classes/class-core.php:327
77
+ msgid "No Event categories found in Trash"
78
+ msgstr "هیچ دسته رویدادی در «حذف شده ها» یافت نشد"
79
+
80
+ #: classes/class-core.php:356 classes/class-core.php:368
81
+ #: classes/class-core.php:372
82
+ msgid "Event tags"
83
+ msgstr "برچسب های رویداد"
84
+
85
+ #: classes/class-core.php:357
86
+ msgid "Event tag"
87
+ msgstr "برچسب رویداد"
88
+
89
+ #: classes/class-core.php:358 classes/class-core.php:359
90
+ msgid "Add New Event tag"
91
+ msgstr "افزودن برچسب جدید رویداد"
92
+
93
+ #: classes/class-core.php:360
94
+ msgid "Edit Event tag"
95
+ msgstr "ویرایش برچسب رویداد"
96
+
97
+ #: classes/class-core.php:361
98
+ msgid "New Event tag"
99
+ msgstr "برچسب جدید رویداد"
100
+
101
+ #: classes/class-core.php:362
102
+ msgid "All Event tags"
103
+ msgstr "همه برچسب های رویداد"
104
+
105
+ #: classes/class-core.php:363
106
+ msgid "View Event tag"
107
+ msgstr "نمایش برچسب رویداد"
108
+
109
+ #: classes/class-core.php:364
110
+ msgid "Search Event tag"
111
+ msgstr "جستجوی برچسب رویداد"
112
+
113
+ #: classes/class-core.php:365
114
+ msgid "No Event tags found"
115
+ msgstr "برچسب رویداد یافت نشد"
116
+
117
+ #: classes/class-core.php:366
118
+ msgid "No Event tags found in Trash"
119
+ msgstr "هیچ برچسب رویدادی در «حذف شده ها» یافت نشد"
120
+
121
+ #: classes/class-core.php:404 classes/class-core.php:416
122
+ #: classes/class-hooks.php:171 classes/class-shortcode.php:256
123
+ msgid "Events"
124
+ msgstr "رویدادها"
125
+
126
+ #: classes/class-core.php:405
127
+ msgid "Event"
128
+ msgstr "رویداد"
129
+
130
+ #: classes/class-core.php:406 classes/class-core.php:407
131
+ msgid "Add New Event"
132
+ msgstr "افزودن رویداد جدید"
133
+
134
+ #: classes/class-core.php:408
135
+ msgid "Edit Event"
136
+ msgstr "ویرایش رویداد"
137
+
138
+ #: classes/class-core.php:409
139
+ msgid "New Event"
140
+ msgstr "رویداد جدید"
141
+
142
+ #: classes/class-core.php:410 classes/class-shortcode.php:69
143
+ #: classes/class-shortcode.php:277
144
+ #: templates-functions/action-shortcode-functions.php:39
145
+ #: templates-functions/action-shortcode-functions.php:52
146
+ #: templates-functions/action-shortcode-functions.php:53
147
+ msgid "All Events"
148
+ msgstr "همه رویدادها"
149
+
150
+ #: classes/class-core.php:411
151
+ msgid "View Event"
152
+ msgstr "نمایش رویداد"
153
+
154
+ #: classes/class-core.php:412
155
+ msgid "Search Event"
156
+ msgstr "جستجوی رویداد"
157
+
158
+ #: classes/class-core.php:413
159
+ msgid "No Events found"
160
+ msgstr "رویدادی یافت نشد"
161
+
162
+ #: classes/class-core.php:414
163
+ msgid "No Events found in Trash"
164
+ msgstr "هیچ رویدادی در «حذف شده ها» یافت نشد"
165
+
166
+ #: classes/class-core.php:438 classes/class-core.php:450
167
+ #: classes/class-hooks.php:173
168
+ msgid "Columns"
169
+ msgstr "ستون ها"
170
+
171
+ #: classes/class-core.php:439 classes/class-shortcode.php:251
172
+ #: templates/events/event-data.php:4
173
+ msgid "Column"
174
+ msgstr "ستون"
175
+
176
+ #: classes/class-core.php:440 classes/class-core.php:441
177
+ msgid "Add New Column"
178
+ msgstr "افزودن ستون جدید"
179
+
180
+ #: classes/class-core.php:442
181
+ msgid "Edit Column"
182
+ msgstr "ویرایش ستون"
183
+
184
+ #: classes/class-core.php:443
185
+ msgid "New Column"
186
+ msgstr "ستون جدید"
187
+
188
+ #: classes/class-core.php:444
189
+ msgid "All Columns"
190
+ msgstr "همه ستون ها"
191
+
192
+ #: classes/class-core.php:445
193
+ msgid "View Column"
194
+ msgstr "نمایش ستون"
195
+
196
+ #: classes/class-core.php:446
197
+ msgid "Search Column"
198
+ msgstr "جستجوی ستون"
199
+
200
+ #: classes/class-core.php:447
201
+ msgid "No Columns found"
202
+ msgstr "هیچ ستونی یافت نشد"
203
+
204
+ #: classes/class-core.php:448
205
+ msgid "No Columns found in Trash"
206
+ msgstr "هیچ ستونی در «حذف شده ها» یافت نشد"
207
+
208
+ #: classes/class-hooks.php:66
209
+ msgid "Timetable Sidebar"
210
+ msgstr "ابزارک جدول زمانی"
211
+
212
+ #: classes/class-hooks.php:68 classes/class-hooks.php:170
213
+ #: classes/class-shortcode.php:356
214
+ msgid "Timetable"
215
+ msgstr "جدول زمانی"
216
+
217
+ #: classes/class-hooks.php:163
218
+ msgid "Import Timetable events, categories, tags and images."
219
+ msgstr "درون ریزی رویدادهای جدول زمانی، دسته بندی ها، برچسب ها و عکس ها"
220
+
221
+ #: classes/class-hooks.php:172
222
+ msgid "Add Event"
223
+ msgstr "افزودن رویداد"
224
+
225
+ #: classes/class-hooks.php:174
226
+ msgid "Add Column"
227
+ msgstr "افزودن ستون"
228
+
229
+ #: classes/class-hooks.php:175
230
+ msgid "Event Categories"
231
+ msgstr "دسته های رویداد"
232
+
233
+ #: classes/class-hooks.php:176
234
+ msgid "Event Tags"
235
+ msgstr "برچسب های رویداد"
236
+
237
+ #: classes/class-hooks.php:177 classes/modules/class-post.php:41
238
+ msgid "Settings"
239
+ msgstr "تنظیمات"
240
+
241
+ #: classes/class-hooks.php:178
242
+ msgid "Export / Import"
243
+ msgstr "درون ریزی/برون ریزی"
244
+
245
+ #: classes/class-shortcode.php:266
246
+ msgid "Hour measure"
247
+ msgstr "اندازه گیری ساعت"
248
+
249
+ #: classes/class-shortcode.php:267 templates/popup/index.php:66
250
+ msgid "Hour (1h)"
251
+ msgstr "ساعت (1 ساعت)"
252
+
253
+ #: classes/class-shortcode.php:267 templates/popup/index.php:67
254
+ msgid "Half hour (30min)"
255
+ msgstr "نیم ساعت (30 دقیقه)"
256
+
257
+ #: classes/class-shortcode.php:267 templates/popup/index.php:68
258
+ msgid "Quarter hour (15min)"
259
+ msgstr "ربع ساعت (15 دقیقه)"
260
+
261
+ #: classes/class-shortcode.php:271
262
+ msgid "Filter style"
263
+ msgstr "سبک فیلتر"
264
+
265
+ #: classes/class-shortcode.php:272
266
+ msgid "Dropdown list"
267
+ msgstr "لیست بازشو"
268
+
269
+ #: classes/class-shortcode.php:272 templates/popup/index.php:77
270
+ msgid "Tabs"
271
+ msgstr "تب ها"
272
+
273
+ #: classes/class-shortcode.php:276
274
+ msgid "Filter label"
275
+ msgstr "برچسب فیلتر"
276
+
277
+ #: classes/class-shortcode.php:281
278
+ msgid "Hide 'All Events' view"
279
+ msgstr "نمایش ندادن همه رویدادها"
280
+
281
+ #: classes/class-shortcode.php:282 classes/class-shortcode.php:287
282
+ #: classes/class-shortcode.php:292 classes/class-shortcode.php:299
283
+ #: classes/class-shortcode.php:305 classes/class-shortcode.php:311
284
+ #: classes/class-shortcode.php:317 classes/class-shortcode.php:323
285
+ #: classes/class-shortcode.php:328 classes/class-shortcode.php:352
286
+ #: templates/events/metabox-event-options.php:45 templates/popup/index.php:93
287
+ #: templates/popup/index.php:102 templates/popup/index.php:112
288
+ #: templates/popup/index.php:120 templates/popup/index.php:129
289
+ #: templates/widgets/gallery-list.php:60
290
+ msgid "No"
291
+ msgstr "خیر"
292
+
293
+ #: classes/class-shortcode.php:282 classes/class-shortcode.php:287
294
+ #: classes/class-shortcode.php:292 classes/class-shortcode.php:299
295
+ #: classes/class-shortcode.php:305 classes/class-shortcode.php:311
296
+ #: classes/class-shortcode.php:317 classes/class-shortcode.php:323
297
+ #: classes/class-shortcode.php:328 classes/class-shortcode.php:352
298
+ #: templates/events/metabox-event-options.php:46 templates/popup/index.php:94
299
+ #: templates/popup/index.php:103 templates/popup/index.php:111
300
+ #: templates/popup/index.php:121 templates/popup/index.php:130
301
+ #: templates/widgets/gallery-list.php:62
302
+ msgid "Yes"
303
+ msgstr "بله"
304
+
305
+ #: classes/class-shortcode.php:286
306
+ msgid "Hide first (hours) column"
307
+ msgstr "پنهان کردن ستون اول (ساعت)"
308
+
309
+ #: classes/class-shortcode.php:291
310
+ msgid "Hide empty rows"
311
+ msgstr "پنهان کردن سطرهای خالی"
312
+
313
+ #: classes/class-shortcode.php:297 templates/popup/index.php:40
314
+ #: templates/widgets/gallery-list.php:3
315
+ msgid "Title"
316
+ msgstr "عنوان"
317
+
318
+ #: classes/class-shortcode.php:303 templates/popup/index.php:41
319
+ msgid "Time"
320
+ msgstr "زمان"
321
+
322
+ #: classes/class-shortcode.php:309 templates/popup/index.php:42
323
+ msgid "Subtitle"
324
+ msgstr "عنوان فرعی"
325
+
326
+ #: classes/class-shortcode.php:315 templates/events/event-data.php:7
327
+ #: templates/popup/index.php:43
328
+ msgid "Description"
329
+ msgstr "توضیحات"
330
+
331
+ #: classes/class-shortcode.php:321
332
+ msgid "User"
333
+ msgstr "کاربر"
334
+
335
+ #: classes/class-shortcode.php:327
336
+ msgid "Disable event URL"
337
+ msgstr "URL رویداد را غیرفعال کنید"
338
+
339
+ #: classes/class-shortcode.php:332
340
+ msgid "Text align"
341
+ msgstr "چیدمان متن"
342
+
343
+ #: classes/class-shortcode.php:333 templates/popup/index.php:137
344
+ msgid "center"
345
+ msgstr "وسط"
346
+
347
+ #: classes/class-shortcode.php:333 templates/popup/index.php:138
348
+ msgid "left"
349
+ msgstr "چپ"
350
+
351
+ #: classes/class-shortcode.php:333 templates/popup/index.php:139
352
+ msgid "right"
353
+ msgstr "راست"
354
+
355
+ #: classes/class-shortcode.php:337
356
+ msgid "Id"
357
+ msgstr "ID"
358
+
359
+ #: classes/class-shortcode.php:341
360
+ msgid "Row height (in px)"
361
+ msgstr "ارتفاع ردیف (در پیکسل)"
362
+
363
+ #: classes/class-shortcode.php:346
364
+ msgid "Base Font Size"
365
+ msgstr "اندازه فونت پایه"
366
+
367
+ #: classes/class-shortcode.php:351
368
+ msgid "Responsive"
369
+ msgstr "واکنش گرایی"
370
+
371
+ #: classes/controllers/class-controller-settings.php:53
372
+ msgid "Settings saved."
373
+ msgstr "تنظیمات ذخیره شد."
374
+
375
+ #: classes/models/class-column.php:42 classes/modules/class-post.php:40
376
+ msgid "Timeslots"
377
+ msgstr "فاصله زمانی"
378
+
379
+ #: classes/models/class-events.php:167
380
+ msgid "Tags"
381
+ msgstr "تگ ها"
382
+
383
+ #: classes/models/class-events.php:168
384
+ msgid "Categories"
385
+ msgstr "دسته ها"
386
+
387
+ #: classes/models/class-import.php:125
388
+ msgid "Assign Authors"
389
+ msgstr "اختصاص دادن نویسندگان"
390
+
391
+ #: classes/models/class-import.php:126
392
+ msgid ""
393
+ "To make it easier for you to edit and save the imported content, you may "
394
+ "want to reassign the author of the imported item to an existing user of this "
395
+ "site. For example, you may want to import all the entries as <code>admin</"
396
+ "code>s entries."
397
+ msgstr ""
398
+ "برای اینکه بتوانید محتوای وارد شده را ویرایش و ذخیره کنید، ممکن است بخواهید "
399
+ "نویسنده اثر وارد شده را به یک کاربر فعلی این سایت مجددا اختصاص دهید.به عنوان "
400
+ "مثال، شما ممکن است بخواهید تمام ورودی ها را به عنوان ورودی <code> مدیر </"
401
+ "code> وارد کنید."
402
+
403
+ #: classes/models/class-import.php:128
404
+ #, php-format
405
+ msgid ""
406
+ "If a new user is created by WordPress, a new password will be randomly "
407
+ "generated and the new user&#8217;s role will be set as %s. Manually changing "
408
+ "the new user&#8217;s details will be necessary."
409
+ msgstr ""
410
+ "اگر یک کاربر جدید توسط وردپرس ایجاد شده باشد، یک رمز عبور جدید به صورت "
411
+ "تصادفی ایجاد خواهد شد و نقش کاربر جدید user&#8217;s به عنوان %s تنظیم خواهد "
412
+ "شد. تغییر دستی اطلاعات جدید کاربر user&#8217;s ضروری است."
413
+
414
+ #: classes/models/class-import.php:138
415
+ msgid "Import Attachments"
416
+ msgstr "درون ریزی پیوست ها"
417
+
418
+ #: classes/models/class-import.php:141
419
+ msgid "Download and import file attachments"
420
+ msgstr "دریافت و درون ریزی فایل های پیوست"
421
+
422
+ #: classes/models/class-import.php:145
423
+ msgid "Submit"
424
+ msgstr "ارسال"
425
+
426
+ #: classes/models/class-import.php:158
427
+ msgid "Import author:"
428
+ msgstr "درون ریزی نویسنده:"
429
+
430
+ #: classes/models/class-import.php:169
431
+ msgid "or create new user with login name:"
432
+ msgstr "یا ایجاد کاربر جدید با نام کاربری:"
433
+
434
+ #: classes/models/class-import.php:172
435
+ msgid "as a new user:"
436
+ msgstr "به عنوان یک کاربر جدید:"
437
+
438
+ #: classes/models/class-import.php:180
439
+ msgid "assign posts to an existing user:"
440
+ msgstr "اختصاص دادن پست به یک کاربر موجود:"
441
+
442
+ #: classes/models/class-import.php:182
443
+ msgid "or assign posts to an existing user:"
444
+ msgstr "یا اختصاص دادن پست به یک کاربر موجود:"
445
+
446
+ #: classes/models/class-import.php:183
447
+ #: templates/column/metabox-column-options.php:18
448
+ msgid "- Select -"
449
+ msgstr "- انتخاب -"
450
+
451
+ #: classes/models/class-import.php:269
452
+ msgid "All done."
453
+ msgstr "همه انجام شد."
454
+
455
+ #: classes/models/class-import.php:269
456
+ msgid "Have fun!"
457
+ msgstr "خوش بگذره!"
458
+
459
+ #: classes/models/class-import.php:270
460
+ msgid "Remember to update the passwords and roles of imported users."
461
+ msgstr ""
462
+ "به خاطر داشته باشید که کلمه عبور و نقش کاربران وارد شده را به روز کنید."
463
+
464
+ #: classes/models/class-import.php:286
465
+ msgid "Fetching attachments is not enabled"
466
+ msgstr "پیوست های دریافتی فعال نیستند"
467
+
468
+ #: classes/models/class-import.php:299
469
+ msgid "Invalid file type"
470
+ msgstr "نوع فایل نامعتبر است"
471
+
472
+ #: classes/models/class-import.php:379
473
+ msgid "Remote server did not respond"
474
+ msgstr "سرور از راه دور جواب نمی دهد"
475
+
476
+ #: classes/models/class-import.php:385
477
+ #, php-format
478
+ msgid "Remote server returned error response %1$d %2$s"
479
+ msgstr "سرور راه دور پاسخ خطا %1$d %2$s را دریافت کرد"
480
+
481
+ #: classes/models/class-import.php:394
482
+ msgid "Remote file is incorrect size"
483
+ msgstr "سایز فایل ارسالی سرور رته دور نادرست است"
484
+
485
+ #: classes/models/class-import.php:399
486
+ msgid "Zero size file downloaded"
487
+ msgstr "سایز فایل دانلود شده صفر می باشد"
488
+
489
+ #: classes/models/class-import.php:405
490
+ #, php-format
491
+ msgid "Remote file is too large, limit is %s"
492
+ msgstr "سایز فایل از راه دور بسیار بزرگ است، سایز به %s محدود شده"
493
+
494
+ #: classes/models/class-import.php:422 classes/models/class-import.php:426
495
+ #: classes/models/class-import.php:436
496
+ msgid "Sorry, there has been an error."
497
+ msgstr "با عرض پوزش، یک خطا وجود دارد."
498
+
499
+ #: classes/models/class-import.php:427
500
+ #, php-format
501
+ msgid ""
502
+ "The export file could not be found at <code>%s</code>. It is likely that "
503
+ "this was caused by a permissions problem."
504
+ msgstr ""
505
+ "فایل درون ریزی در <code>%s</code> یافت نمی شود. احتمالا این مسئله توسط یک "
506
+ "مشکل مجوز ایجاد شده است."
507
+
508
+ #: classes/models/class-import.php:444
509
+ #, php-format
510
+ msgid ""
511
+ "This WXR file (version %s) may not be supported by this version of the "
512
+ "importer. Please consider updating."
513
+ msgstr ""
514
+ "این فایل WXR (نسخه %s) ممکن است توسط این نسخه از وارد کننده پشتیبانی نشود. "
515
+ "لطفا به روزرسانی کنید."
516
+
517
+ #: classes/models/class-import.php:501
518
+ #, php-format
519
+ msgid ""
520
+ "Failed to import author %s. Their posts will be attributed to the current "
521
+ "user."
522
+ msgstr ""
523
+ "وارد کردن نویسنده %s امکان پذیر نبود. پست های آن به کاربر فعلی نسبت داده می "
524
+ "شود."
525
+
526
+ #: classes/models/class-import.php:558
527
+ #, php-format
528
+ msgid ""
529
+ "Failed to create new user for %s. Their posts will be attributed to the "
530
+ "current user."
531
+ msgstr ""
532
+ "امکان ایجاد کاربر جدید برای %s نبود. پست های آن به کاربر فعلی نسبت داده می "
533
+ "شود."
534
+
535
+ #: classes/models/class-import.php:609 classes/models/class-import.php:766
536
+ #, php-format
537
+ msgid "Failed to import %s %s"
538
+ msgstr "درون ریزی نا موفق بود %s %s"
539
+
540
+ #: classes/models/class-import.php:656
541
+ #, php-format
542
+ msgid "Failed to import &#8220;%s&#8221;: Invalid post type %s"
543
+ msgstr "وارد کردن &#8220;%s&#8221;: ناموفق بود. نوع پست %s نامعتبر است"
544
+
545
+ #: classes/models/class-import.php:673
546
+ #, php-format
547
+ msgid "%s &#8220;%s&#8221; already exists."
548
+ msgstr "%s &#8220;%s&#8221; درحال حاضر وجود دارد."
549
+
550
+ #: classes/models/class-import.php:732
551
+ #, php-format
552
+ msgid "Failed to import %s &#8220;%s&#8221;"
553
+ msgstr "%s &#8220;%s&#8221; وارد کردن نشد"
554
+
555
+ #: classes/modules/class-post.php:42
556
+ msgid "Column Type"
557
+ msgstr "نوع ستون"
558
+
559
+ #: classes/widgets/class-mp-timetable-widget.php:22
560
+ msgid "Display upcoming events."
561
+ msgstr "نمایش رویدادهای آینده"
562
+
563
+ #: classes/widgets/class-mp-timetable-widget.php:24
564
+ msgid "Timetable Events"
565
+ msgstr "جدول زمانی رویدادها"
566
+
567
+ #: templates-functions/actions-mp-event-functions.php:27
568
+ #: templates/theme/event-timeslots.php:7
569
+ #, php-format
570
+ msgid "Event Timeslots (%s)"
571
+ msgstr "فاصله زمانی رویداد (%s)"
572
+
573
+ #: templates/column/metabox-column-options.php:9
574
+ msgid "Simple Column"
575
+ msgstr "ستون ساده"
576
+
577
+ #: templates/column/metabox-column-options.php:15
578
+ msgid "Day"
579
+ msgstr "روز"
580
+
581
+ #: templates/column/metabox-column-options.php:19
582
+ msgid "Sunday"
583
+ msgstr "یک شنبه"
584
+
585
+ #: templates/column/metabox-column-options.php:20
586
+ msgid "Monday"
587
+ msgstr "دوشنبه"
588
+
589
+ #: templates/column/metabox-column-options.php:21
590
+ msgid "Tuesday"
591
+ msgstr "سه شنبه"
592
+
593
+ #: templates/column/metabox-column-options.php:22
594
+ msgid "Wednesday"
595
+ msgstr "چهارشنبه"
596
+
597
+ #: templates/column/metabox-column-options.php:23
598
+ msgid "Thursday"
599
+ msgstr "پنج شنبه"
600
+
601
+ #: templates/column/metabox-column-options.php:24
602
+ msgid "Friday"
603
+ msgstr "جمعه"
604
+
605
+ #: templates/column/metabox-column-options.php:25
606
+ msgid "Saturday"
607
+ msgstr "شنبه"
608
+
609
+ #: templates/column/metabox-column-options.php:32
610
+ msgid "Date"
611
+ msgstr "تاریخ"
612
+
613
+ #: templates/events/event-data.php:5
614
+ msgid "Start"
615
+ msgstr "شروع"
616
+
617
+ #: templates/events/event-data.php:6
618
+ msgid "End"
619
+ msgstr "پایان"
620
+
621
+ #: templates/events/event-data.php:8
622
+ msgid "Head"
623
+ msgstr "مسئول"
624
+
625
+ #: templates/events/event-data.php:9
626
+ msgid "Actions"
627
+ msgstr "اقدامات"
628
+
629
+ #: templates/events/event-data.php:32
630
+ msgid "Edit event in the form below"
631
+ msgstr "رویداد را در فرم زیر ویرایش کنید"
632
+
633
+ #: templates/events/event-data.php:33
634
+ msgid "Delete"
635
+ msgstr "حذف"
636
+
637
+ #: templates/events/metabox-event-data.php:10
638
+ msgid "Column:"
639
+ msgstr "ستون:"
640
+
641
+ #: templates/events/metabox-event-data.php:18
642
+ #, php-format
643
+ msgid "Select column or <a target=\"_blank\" href=\"%s\">Add New</a>."
644
+ msgstr "انتخاب یک ستون<a target=\"_blank\" href=\"%s\">افزودن ستون جدید</a>."
645
+
646
+ #: templates/events/metabox-event-data.php:20
647
+ #, php-format
648
+ msgid "No columns found. <a href=\"%s\">Create at least one column first.</a>"
649
+ msgstr "ستون یافت نشد. <a href=\"%s\">ابتدا حداقل یک ستون ایجاد کنید.</a>"
650
+
651
+ #: templates/events/metabox-event-data.php:26
652
+ msgid "Start Time:"
653
+ msgstr "زمان شروع:"
654
+
655
+ #: templates/events/metabox-event-data.php:29
656
+ #: templates/events/metabox-event-data.php:36
657
+ msgid "hh:mm"
658
+ msgstr "hh:mm"
659
+
660
+ #: templates/events/metabox-event-data.php:33
661
+ msgid "End Time:"
662
+ msgstr "زمان اتمام:"
663
+
664
+ #: templates/events/metabox-event-data.php:40
665
+ msgid "Description:"
666
+ msgstr "توضیحات:"
667
+
668
+ #: templates/events/metabox-event-data.php:44
669
+ msgid "Event Head:"
670
+ msgstr "مسئول رویداد:"
671
+
672
+ #: templates/events/metabox-event-data.php:52
673
+ msgid "none"
674
+ msgstr "هیچکدام (خالی)"
675
+
676
+ #: templates/events/metabox-event-data.php:75
677
+ msgid "Add New"
678
+ msgstr "مورد جدیدی اضافه کنید"
679
+
680
+ #: templates/events/metabox-event-options.php:4
681
+ msgid "Event Subtitle:"
682
+ msgstr "عنوان فرعی رویداد:"
683
+
684
+ #: templates/events/metabox-event-options.php:8
685
+ msgid "Background Color:"
686
+ msgstr "رنگ پس زمینه:"
687
+
688
+ #: templates/events/metabox-event-options.php:15
689
+ msgid "Background Hover Color:"
690
+ msgstr "رنگ دور پس زمینه:"
691
+
692
+ #: templates/events/metabox-event-options.php:22
693
+ msgid "Text Color:"
694
+ msgstr "رنگ متن:"
695
+
696
+ #: templates/events/metabox-event-options.php:29
697
+ msgid "Text Hover Color:"
698
+ msgstr "رنگ سایه متن:"
699
+
700
+ #: templates/events/metabox-event-options.php:36
701
+ msgid "Custom Event URL:"
702
+ msgstr "سفارشی کردن URL رویداد:"
703
+
704
+ #: templates/events/metabox-event-options.php:42
705
+ msgid "Disable link to this event:"
706
+ msgstr "غیر فعال کردن لینک به این رویداد:"
707
+
708
+ #: templates/popup/index.php:5
709
+ msgid "<b>Columns</b> (required)"
710
+ msgstr "<b>ستون</b> (ضروری)"
711
+
712
+ #: templates/popup/index.php:12 templates/popup/index.php:23
713
+ #: templates/popup/index.php:34
714
+ msgid "In order to display multiple points hold ctrl/cmd button."
715
+ msgstr "برای نمایش چندین نقطه دکمه ctrl / cmd را نگه دارید."
716
+
717
+ #: templates/popup/index.php:16
718
+ msgid "Specific <b>events</b>"
719
+ msgstr "<b>رویدادهای</b> ویژه"
720
+
721
+ #: templates/popup/index.php:27
722
+ msgid "Event <b>categories</b>"
723
+ msgstr "<b>دسته های</b> رویداد"
724
+
725
+ #: templates/popup/index.php:38
726
+ msgid "Fields to display:"
727
+ msgstr "زمینه نمایش:"
728
+
729
+ #: templates/popup/index.php:44
730
+ msgid "Event Head"
731
+ msgstr "مسئول رویداد"
732
+
733
+ #: templates/popup/index.php:45
734
+ msgid "Check the event parameter(s) to be displayed in the timetable."
735
+ msgstr "پارامترهای رویداد را در جدول زمانبندی نمایش دهید."
736
+
737
+ #: templates/popup/index.php:49
738
+ msgid "Block height in pixels"
739
+ msgstr "ارتفاع بلوک به صورت پیکسل"
740
+
741
+ #: templates/popup/index.php:52
742
+ msgid "Set height of the block"
743
+ msgstr "ارتفاع بلوک را تنظیم کنید"
744
+
745
+ #: templates/popup/index.php:56
746
+ msgid "Base font size"
747
+ msgstr "اندازه فونت پایه"
748
+
749
+ #: templates/popup/index.php:59
750
+ msgid "Base font size for the table. Example 12px, 2em, 80%."
751
+ msgstr "اندازه فونت پایه برای جدول. مثال 12px، 2em، 80٪."
752
+
753
+ #: templates/popup/index.php:63
754
+ msgid "Time frame for event"
755
+ msgstr "نحوه نمایش زمان برای رویداد"
756
+
757
+ #: templates/popup/index.php:73
758
+ msgid "Filter events style"
759
+ msgstr "سبک فیلتر رویدادها"
760
+
761
+ #: templates/popup/index.php:76
762
+ msgid "Dropdown"
763
+ msgstr "کشویی"
764
+
765
+ #: templates/popup/index.php:78
766
+ msgid "None"
767
+ msgstr "هیچکدام"
768
+
769
+ #: templates/popup/index.php:83
770
+ msgid "Filter title to display all events"
771
+ msgstr "عنوان فیلتر برای نمایش همه رویدادها"
772
+
773
+ #: templates/popup/index.php:89
774
+ msgid "Hide 'All Events' option"
775
+ msgstr "پنهان کردن گزینه \"همه رویدادها\""
776
+
777
+ #: templates/popup/index.php:99
778
+ msgid "Hide column with hours"
779
+ msgstr "پنهان کردن ستون با ساعت"
780
+
781
+ #: templates/popup/index.php:108
782
+ msgid "Do not display empty rows"
783
+ msgstr "ردیف های خالی را نمایش ندهید"
784
+
785
+ #: templates/popup/index.php:117
786
+ msgid "Merge cells with common events"
787
+ msgstr "ادغام سلول ها با رویدادهای مشترک"
788
+
789
+ #: templates/popup/index.php:126 templates/widgets/gallery-list.php:56
790
+ msgid "Disable event link"
791
+ msgstr "پیوند رویداد را غیرفعال کنید"
792
+
793
+ #: templates/popup/index.php:135
794
+ msgid "Horizontal align"
795
+ msgstr "تراز چیدمان افقی"
796
+
797
+ #: templates/popup/index.php:144
798
+ msgid "Vertical align"
799
+ msgstr "تراز چیدمان عمودی"
800
+
801
+ #: templates/popup/index.php:146
802
+ msgid "default"
803
+ msgstr "پیشفرض"
804
+
805
+ #: templates/popup/index.php:147
806
+ msgid "top"
807
+ msgstr "بالا"
808
+
809
+ #: templates/popup/index.php:148
810
+ msgid "middle"
811
+ msgstr "وسط"
812
+
813
+ #: templates/popup/index.php:149
814
+ msgid "bottom"
815
+ msgstr "پایین"
816
+
817
+ #: templates/popup/index.php:154
818
+ msgid "Unique ID"
819
+ msgstr "شناسه منحصر به فرد"
820
+
821
+ #: templates/popup/index.php:157
822
+ msgid ""
823
+ "If you use more than one table on a page specify the unique ID for a "
824
+ "timetable. It is usually all lowercase and contains only letters, numbers, "
825
+ "and hyphens."
826
+ msgstr ""
827
+ "اگر بیش از یک جدول در یک صفحه استفاده کنید، شناسه منحصر به فرد برای یک جدول "
828
+ "زمانی را مشخص کنید. شناسه منحصر به فرد به صورت حروف کوچک است و فقط شامل "
829
+ "حروف، اعداد و خطوط است."
830
+
831
+ #: templates/popup/index.php:161
832
+ msgid "CSS class"
833
+ msgstr "کلاس CSS"
834
+
835
+ #: templates/popup/index.php:167
836
+ msgid "Mobile behavior"
837
+ msgstr "نحوه نمایش در موبایل"
838
+
839
+ #: templates/popup/index.php:170
840
+ msgid "List"
841
+ msgstr "لیست"
842
+
843
+ #: templates/popup/index.php:171
844
+ msgid "Table"
845
+ msgstr "جدول"
846
+
847
+ #: templates/popup/index.php:173
848
+ msgid ""
849
+ "Tick \"List\" to display events in a list view on mobile devices. Tick "
850
+ "\"Table\" to display events in a table."
851
+ msgstr ""
852
+ "با کلیک بر روی \"لیست\" رویدادها به صورت لیست زیر هم در موبایل به نمایش در "
853
+ "می آید. و با انتخاب \"جدول\" رویدادها در یک جدول به نمایش گذاشته می شود.."
854
+
855
+ #: templates/popup/index.php:179
856
+ msgid "Add Timetable"
857
+ msgstr "افزودن جدول زمانی"
858
+
859
+ #: templates/settings/general.php:1
860
+ msgid "General Settings"
861
+ msgstr "تنظیمات عمومی"
862
+
863
+ #: templates/settings/general.php:8
864
+ msgid "Template Mode"
865
+ msgstr "حالت الگو"
866
+
867
+ #: templates/settings/general.php:12
868
+ msgid "Theme Mode"
869
+ msgstr "حالت قالب"
870
+
871
+ #: templates/settings/general.php:13
872
+ msgid "Developer Mode"
873
+ msgstr "حالت توسعه دهنده"
874
+
875
+ #: templates/settings/general.php:15
876
+ msgid ""
877
+ "Choose Theme Mode to display the content with the styles of your theme. "
878
+ "Choose Developer Mode to control appearance of the content with custom page "
879
+ "templates, actions and filters."
880
+ msgstr ""
881
+ "اگر میخواهید تا محتوا به سبک قالب فعلی به نمایش گذاشته شود \"حالت قالب\" را "
882
+ "انتخاب کنید. اگر حالت توسعه دهنده را انتخاب کنید نحوه نمایش را کاملا می "
883
+ "توانید سفارشی کنید."
884
+
885
+ #: templates/settings/general.php:15
886
+ msgid ""
887
+ "This option can't be changed if your theme is initially integrated with the "
888
+ "plugin."
889
+ msgstr ""
890
+ "این گزینه را نمی توان تغییر داد اگر موضوع شما در ابتدا با افزونه ادغام شود."
891
+
892
+ #: templates/settings/general.php:20
893
+ msgid "Save"
894
+ msgstr "ذخیره"
895
+
896
+ #: templates/shortcodes/empty-search-events.php:1
897
+ #: templates/theme/widget-upcoming-view.php:54
898
+ #: templates/widgets/widget-view.php:91
899
+ msgid "no events found"
900
+ msgstr "رویدادی یافت نشد"
901
+
902
+ #: templates/templates-actions/action-sidebar.php:8
903
+ #: templates/widgets/gallery-list.php:14
904
+ msgid "Today upcoming events"
905
+ msgstr "رویدادهای آینده در این روز"
906
+
907
+ #: templates/templates-actions/action-sidebar.php:13
908
+ #: templates/widgets/gallery-list.php:16
909
+ msgid "All upcoming events"
910
+ msgstr "همه رویدادهای آینده"
911
+
912
+ #: templates/widgets/gallery-list.php:10
913
+ msgid "Events to display"
914
+ msgstr "رویدادهای برای نمایش"
915
+
916
+ #: templates/widgets/gallery-list.php:18
917
+ msgid "Ongoing events"
918
+ msgstr "رویدادهای پیش رو"
919
+
920
+ #: templates/widgets/gallery-list.php:23
921
+ msgid "Input number of days"
922
+ msgstr "تعداد ورودی ها در روز"
923
+
924
+ #: templates/widgets/gallery-list.php:28
925
+ msgid "day"
926
+ msgstr "روز"
927
+
928
+ #: templates/widgets/gallery-list.php:32
929
+ msgid "Event categories. Leave blank to display all."
930
+ msgstr "دسته های رویداد، برای نمایش همه خالی بگذارید"
931
+
932
+ #: templates/widgets/gallery-list.php:49
933
+ msgid "Number of events to display"
934
+ msgstr "تعداد رویداد قایل نمایش"
935
+
936
+ #: templates/widgets/gallery-list.php:67
937
+ msgid "Custom link for events"
938
+ msgstr "آدرس سفارشی برای رویداد"
939
+
940
+ #: templates/widgets/gallery-list.php:75
941
+ msgid "Event background color"
942
+ msgstr "رنگ پشت زمینه رویداد"
943
+
944
+ #: templates/widgets/gallery-list.php:85
945
+ msgid "Event background color on hover"
946
+ msgstr "رنگ سایه پشت زمینه رویداد"
947
+
948
+ #: templates/widgets/gallery-list.php:95
949
+ msgid "Event text color"
950
+ msgstr "رنگ متن رویداد"
951
+
952
+ #: templates/widgets/gallery-list.php:105
953
+ msgid "Event text color on hover"
954
+ msgstr "رنگ سایه متن رویداد"
955
+
956
+ #: templates/widgets/gallery-list.php:115
957
+ msgid "Event border color"
958
+ msgstr "رنگ کادر دور رویداد"
959
+
960
+ #: templates/widgets/gallery-list.php:125
961
+ msgid "Event border color on hover"
962
+ msgstr "رنگ سایه کادر دور رویداد"
languages/mp-timetable.pot ADDED
@@ -0,0 +1,1008 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #, fuzzy
2
+ msgid ""
3
+ msgstr ""
4
+ "Project-Id-Version: Timetable and Event Schedule Plugin\n"
5
+ "POT-Creation-Date: 2019-04-04 15:08+0300\n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: \n"
8
+ "Language-Team: MotoPress <info@getmotopress.com>\n"
9
+ "Language: en\n"
10
+ "MIME-Version: 1.0\n"
11
+ "Content-Type: text/plain; charset=UTF-8\n"
12
+ "Content-Transfer-Encoding: 8bit\n"
13
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
14
+ "X-Generator: Poedit 2.2.1\n"
15
+ "X-Poedit-SourceCharset: UTF-8\n"
16
+ "X-Poedit-KeywordsList: esc_attr__;esc_html__;esc_attr_e;__;_e;_n;_x\n"
17
+ "X-Poedit-Basepath: ..\n"
18
+ "X-Poedit-SearchPath-0: .\n"
19
+ "X-Poedit-SearchPathExcluded-0: classes/libs\n"
20
+ "X-Poedit-SearchPathExcluded-1: node_modules\n"
21
+
22
+ #: admin/help/index.php:11
23
+ #, php-format
24
+ msgid "If you like %1$s please leave us a %2$s rating."
25
+ msgstr ""
26
+
27
+ #: admin/import/export.php:1 admin/import/export.php:5
28
+ msgid "Export"
29
+ msgstr ""
30
+
31
+ #: admin/import/greet.php:2
32
+ msgid "Choose a WXR (.xml) file to upload, then click Upload file and import."
33
+ msgstr ""
34
+
35
+ #: admin/import/header.php:2
36
+ msgid "Import / Export Timetable Plugin Data"
37
+ msgstr ""
38
+
39
+ #: admin/import/import.php:1
40
+ msgid "Import"
41
+ msgstr ""
42
+
43
+ #: classes/blocks/class-timetable-block.php:140 classes/class-core.php:440
44
+ #: classes/class-shortcode.php:69 classes/class-shortcode.php:277
45
+ #: templates-functions/action-shortcode-functions.php:39
46
+ #: templates-functions/action-shortcode-functions.php:52
47
+ #: templates-functions/action-shortcode-functions.php:53
48
+ msgid "All Events"
49
+ msgstr ""
50
+
51
+ #: classes/class-core.php:330 classes/class-core.php:341
52
+ #: classes/class-core.php:345 classes/class-shortcode.php:261
53
+ #: media/js/blocks/src/timetable/inspector.js:104
54
+ msgid "Event categories"
55
+ msgstr ""
56
+
57
+ #: classes/class-core.php:331
58
+ msgid "Event category"
59
+ msgstr ""
60
+
61
+ #: classes/class-core.php:332 classes/class-core.php:333
62
+ msgid "Add New Event category"
63
+ msgstr ""
64
+
65
+ #: classes/class-core.php:334
66
+ msgid "Edit Event category"
67
+ msgstr ""
68
+
69
+ #: classes/class-core.php:335
70
+ msgid "New Event category"
71
+ msgstr ""
72
+
73
+ #: classes/class-core.php:336
74
+ msgid "All Event categories"
75
+ msgstr ""
76
+
77
+ #: classes/class-core.php:337
78
+ msgid "View Event category"
79
+ msgstr ""
80
+
81
+ #: classes/class-core.php:338
82
+ msgid "Search Event category"
83
+ msgstr ""
84
+
85
+ #: classes/class-core.php:339
86
+ msgid "No Event categories found"
87
+ msgstr ""
88
+
89
+ #: classes/class-core.php:340
90
+ msgid "No Event categories found in Trash"
91
+ msgstr ""
92
+
93
+ #: classes/class-core.php:374 classes/class-core.php:385
94
+ #: classes/class-core.php:389
95
+ msgid "Event tags"
96
+ msgstr ""
97
+
98
+ #: classes/class-core.php:375
99
+ msgid "Event tag"
100
+ msgstr ""
101
+
102
+ #: classes/class-core.php:376 classes/class-core.php:377
103
+ msgid "Add New Event tag"
104
+ msgstr ""
105
+
106
+ #: classes/class-core.php:378
107
+ msgid "Edit Event tag"
108
+ msgstr ""
109
+
110
+ #: classes/class-core.php:379
111
+ msgid "New Event tag"
112
+ msgstr ""
113
+
114
+ #: classes/class-core.php:380
115
+ msgid "All Event tags"
116
+ msgstr ""
117
+
118
+ #: classes/class-core.php:381
119
+ msgid "View Event tag"
120
+ msgstr ""
121
+
122
+ #: classes/class-core.php:382
123
+ msgid "Search Event tag"
124
+ msgstr ""
125
+
126
+ #: classes/class-core.php:383
127
+ msgid "No Event tags found"
128
+ msgstr ""
129
+
130
+ #: classes/class-core.php:384
131
+ msgid "No Event tags found in Trash"
132
+ msgstr ""
133
+
134
+ #: classes/class-core.php:434 classes/class-core.php:445
135
+ #: classes/class-hooks.php:181 classes/class-shortcode.php:256
136
+ msgid "Events"
137
+ msgstr ""
138
+
139
+ #: classes/class-core.php:435
140
+ msgid "Event"
141
+ msgstr ""
142
+
143
+ #: classes/class-core.php:436 classes/class-core.php:437
144
+ msgid "Add New Event"
145
+ msgstr ""
146
+
147
+ #: classes/class-core.php:438
148
+ msgid "Edit Event"
149
+ msgstr ""
150
+
151
+ #: classes/class-core.php:439
152
+ msgid "New Event"
153
+ msgstr ""
154
+
155
+ #: classes/class-core.php:441
156
+ msgid "View Event"
157
+ msgstr ""
158
+
159
+ #: classes/class-core.php:442
160
+ msgid "Search Event"
161
+ msgstr ""
162
+
163
+ #: classes/class-core.php:443
164
+ msgid "No Events found"
165
+ msgstr ""
166
+
167
+ #: classes/class-core.php:444
168
+ msgid "No Events found in Trash"
169
+ msgstr ""
170
+
171
+ #: classes/class-core.php:473 classes/class-core.php:484
172
+ #: classes/class-hooks.php:189 media/js/blocks/src/timetable/inspector.js:87
173
+ msgid "Columns"
174
+ msgstr ""
175
+
176
+ #: classes/class-core.php:474 classes/class-shortcode.php:251
177
+ #: templates/events/event-data.php:4
178
+ msgid "Column"
179
+ msgstr ""
180
+
181
+ #: classes/class-core.php:475 classes/class-core.php:476
182
+ msgid "Add New Column"
183
+ msgstr ""
184
+
185
+ #: classes/class-core.php:477
186
+ msgid "Edit Column"
187
+ msgstr ""
188
+
189
+ #: classes/class-core.php:478
190
+ msgid "New Column"
191
+ msgstr ""
192
+
193
+ #: classes/class-core.php:479
194
+ msgid "All Columns"
195
+ msgstr ""
196
+
197
+ #: classes/class-core.php:480
198
+ msgid "View Column"
199
+ msgstr ""
200
+
201
+ #: classes/class-core.php:481
202
+ msgid "Search Column"
203
+ msgstr ""
204
+
205
+ #: classes/class-core.php:482
206
+ msgid "No Columns found"
207
+ msgstr ""
208
+
209
+ #: classes/class-core.php:483
210
+ msgid "No Columns found in Trash"
211
+ msgstr ""
212
+
213
+ #: classes/class-hooks.php:67
214
+ msgid "Timetable Sidebar"
215
+ msgstr ""
216
+
217
+ #: classes/class-hooks.php:69 classes/class-hooks.php:177
218
+ #: classes/class-shortcode.php:356 media/js/blocks/src/timetable/index.js:12
219
+ msgid "Timetable"
220
+ msgstr ""
221
+
222
+ #: classes/class-hooks.php:168
223
+ msgid "Import Timetable events, categories, tags and images."
224
+ msgstr ""
225
+
226
+ #: classes/class-hooks.php:185
227
+ msgid "Add Event"
228
+ msgstr ""
229
+
230
+ #: classes/class-hooks.php:193
231
+ msgid "Add Column"
232
+ msgstr ""
233
+
234
+ #: classes/class-hooks.php:197
235
+ msgid "Event Categories"
236
+ msgstr ""
237
+
238
+ #: classes/class-hooks.php:201
239
+ msgid "Event Tags"
240
+ msgstr ""
241
+
242
+ #: classes/class-hooks.php:205 classes/modules/class-post.php:41
243
+ #: media/js/blocks/src/timetable/inspector.js:82
244
+ msgid "Settings"
245
+ msgstr ""
246
+
247
+ #: classes/class-hooks.php:209
248
+ msgid "Export / Import"
249
+ msgstr ""
250
+
251
+ #: classes/class-hooks.php:213
252
+ msgid "Help"
253
+ msgstr ""
254
+
255
+ #: classes/class-permalinks.php:26
256
+ msgid "Timetable Permalinks"
257
+ msgstr ""
258
+
259
+ #: classes/class-permalinks.php:30
260
+ msgid "Column base"
261
+ msgstr ""
262
+
263
+ #: classes/class-permalinks.php:37
264
+ msgid "Event base"
265
+ msgstr ""
266
+
267
+ #: classes/class-permalinks.php:44
268
+ msgid "Event Category base"
269
+ msgstr ""
270
+
271
+ #: classes/class-permalinks.php:51
272
+ msgid "Event Tag base"
273
+ msgstr ""
274
+
275
+ #: classes/class-shortcode.php:266
276
+ msgid "Hour measure"
277
+ msgstr ""
278
+
279
+ #: classes/class-shortcode.php:267
280
+ #: media/js/blocks/src/timetable/inspector.js:162 templates/popup/index.php:66
281
+ msgid "Hour (1h)"
282
+ msgstr ""
283
+
284
+ #: classes/class-shortcode.php:267
285
+ #: media/js/blocks/src/timetable/inspector.js:163 templates/popup/index.php:67
286
+ msgid "Half hour (30min)"
287
+ msgstr ""
288
+
289
+ #: classes/class-shortcode.php:267
290
+ #: media/js/blocks/src/timetable/inspector.js:164 templates/popup/index.php:68
291
+ msgid "Quarter hour (15min)"
292
+ msgstr ""
293
+
294
+ #: classes/class-shortcode.php:271
295
+ msgid "Filter style"
296
+ msgstr ""
297
+
298
+ #: classes/class-shortcode.php:272
299
+ #: media/js/blocks/src/timetable/inspector.js:172
300
+ msgid "Dropdown list"
301
+ msgstr ""
302
+
303
+ #: classes/class-shortcode.php:272
304
+ #: media/js/blocks/src/timetable/inspector.js:173 templates/popup/index.php:77
305
+ msgid "Tabs"
306
+ msgstr ""
307
+
308
+ #: classes/class-shortcode.php:276
309
+ msgid "Filter label"
310
+ msgstr ""
311
+
312
+ #: classes/class-shortcode.php:281
313
+ msgid "Hide 'All Events' view"
314
+ msgstr ""
315
+
316
+ #: classes/class-shortcode.php:282 classes/class-shortcode.php:287
317
+ #: classes/class-shortcode.php:292 classes/class-shortcode.php:299
318
+ #: classes/class-shortcode.php:305 classes/class-shortcode.php:311
319
+ #: classes/class-shortcode.php:317 classes/class-shortcode.php:323
320
+ #: classes/class-shortcode.php:328 classes/class-shortcode.php:352
321
+ #: media/js/blocks/src/timetable/inspector.js:186
322
+ #: media/js/blocks/src/timetable/inspector.js:195
323
+ #: media/js/blocks/src/timetable/inspector.js:204
324
+ #: media/js/blocks/src/timetable/inspector.js:213
325
+ #: media/js/blocks/src/timetable/inspector.js:222
326
+ #: templates/events/metabox-event-options.php:45 templates/popup/index.php:93
327
+ #: templates/popup/index.php:102 templates/popup/index.php:112
328
+ #: templates/popup/index.php:120 templates/popup/index.php:129
329
+ #: templates/widgets/gallery-list.php:60
330
+ msgid "No"
331
+ msgstr ""
332
+
333
+ #: classes/class-shortcode.php:282 classes/class-shortcode.php:287
334
+ #: classes/class-shortcode.php:292 classes/class-shortcode.php:299
335
+ #: classes/class-shortcode.php:305 classes/class-shortcode.php:311
336
+ #: classes/class-shortcode.php:317 classes/class-shortcode.php:323
337
+ #: classes/class-shortcode.php:328 classes/class-shortcode.php:352
338
+ #: media/js/blocks/src/timetable/inspector.js:187
339
+ #: media/js/blocks/src/timetable/inspector.js:196
340
+ #: media/js/blocks/src/timetable/inspector.js:205
341
+ #: media/js/blocks/src/timetable/inspector.js:214
342
+ #: media/js/blocks/src/timetable/inspector.js:223
343
+ #: templates/events/metabox-event-options.php:46 templates/popup/index.php:94
344
+ #: templates/popup/index.php:103 templates/popup/index.php:111
345
+ #: templates/popup/index.php:121 templates/popup/index.php:130
346
+ #: templates/widgets/gallery-list.php:62
347
+ msgid "Yes"
348
+ msgstr ""
349
+
350
+ #: classes/class-shortcode.php:286
351
+ msgid "Hide first (hours) column"
352
+ msgstr ""
353
+
354
+ #: classes/class-shortcode.php:291
355
+ msgid "Hide empty rows"
356
+ msgstr ""
357
+
358
+ #: classes/class-shortcode.php:297
359
+ #: media/js/blocks/src/timetable/inspector.js:110 templates/popup/index.php:40
360
+ #: templates/widgets/gallery-list.php:3
361
+ msgid "Title"
362
+ msgstr ""
363
+
364
+ #: classes/class-shortcode.php:303
365
+ #: media/js/blocks/src/timetable/inspector.js:117 templates/popup/index.php:41
366
+ msgid "Time"
367
+ msgstr ""
368
+
369
+ #: classes/class-shortcode.php:309
370
+ #: media/js/blocks/src/timetable/inspector.js:122 templates/popup/index.php:42
371
+ msgid "Subtitle"
372
+ msgstr ""
373
+
374
+ #: classes/class-shortcode.php:315
375
+ #: media/js/blocks/src/timetable/inspector.js:127
376
+ #: templates/events/event-data.php:7 templates/popup/index.php:43
377
+ msgid "Description"
378
+ msgstr ""
379
+
380
+ #: classes/class-shortcode.php:321
381
+ msgid "User"
382
+ msgstr ""
383
+
384
+ #: classes/class-shortcode.php:327
385
+ msgid "Disable event URL"
386
+ msgstr ""
387
+
388
+ #: classes/class-shortcode.php:332
389
+ msgid "Text align"
390
+ msgstr ""
391
+
392
+ #: classes/class-shortcode.php:333
393
+ #: media/js/blocks/src/timetable/inspector.js:231 templates/popup/index.php:137
394
+ msgid "center"
395
+ msgstr ""
396
+
397
+ #: classes/class-shortcode.php:333
398
+ #: media/js/blocks/src/timetable/inspector.js:232 templates/popup/index.php:138
399
+ msgid "left"
400
+ msgstr ""
401
+
402
+ #: classes/class-shortcode.php:333
403
+ #: media/js/blocks/src/timetable/inspector.js:233 templates/popup/index.php:139
404
+ msgid "right"
405
+ msgstr ""
406
+
407
+ #: classes/class-shortcode.php:337
408
+ msgid "Id"
409
+ msgstr ""
410
+
411
+ #: classes/class-shortcode.php:341
412
+ msgid "Row height (in px)"
413
+ msgstr ""
414
+
415
+ #: classes/class-shortcode.php:346
416
+ msgid "Base Font Size"
417
+ msgstr ""
418
+
419
+ #: classes/class-shortcode.php:351
420
+ msgid "Responsive"
421
+ msgstr ""
422
+
423
+ #: classes/controllers/class-controller-settings.php:53
424
+ msgid "Settings saved."
425
+ msgstr ""
426
+
427
+ #: classes/models/class-column.php:42 classes/modules/class-post.php:40
428
+ msgid "Timeslots"
429
+ msgstr ""
430
+
431
+ #: classes/models/class-events.php:167
432
+ msgid "Tags"
433
+ msgstr ""
434
+
435
+ #: classes/models/class-events.php:168
436
+ msgid "Categories"
437
+ msgstr ""
438
+
439
+ #: classes/models/class-import.php:125
440
+ msgid "Assign Authors"
441
+ msgstr ""
442
+
443
+ #: classes/models/class-import.php:126
444
+ msgid ""
445
+ "To make it easier for you to edit and save the imported content, you may "
446
+ "want to reassign the author of the imported item to an existing user of this "
447
+ "site. For example, you may want to import all the entries as <code>admin</"
448
+ "code>s entries."
449
+ msgstr ""
450
+
451
+ #: classes/models/class-import.php:128
452
+ #, php-format
453
+ msgid ""
454
+ "If a new user is created by WordPress, a new password will be randomly "
455
+ "generated and the new user&#8217;s role will be set as %s. Manually changing "
456
+ "the new user&#8217;s details will be necessary."
457
+ msgstr ""
458
+
459
+ #: classes/models/class-import.php:138
460
+ msgid "Import Attachments"
461
+ msgstr ""
462
+
463
+ #: classes/models/class-import.php:141
464
+ msgid "Download and import file attachments"
465
+ msgstr ""
466
+
467
+ #: classes/models/class-import.php:145
468
+ msgid "Submit"
469
+ msgstr ""
470
+
471
+ #: classes/models/class-import.php:158
472
+ msgid "Import author:"
473
+ msgstr ""
474
+
475
+ #: classes/models/class-import.php:169
476
+ msgid "or create new user with login name:"
477
+ msgstr ""
478
+
479
+ #: classes/models/class-import.php:172
480
+ msgid "as a new user:"
481
+ msgstr ""
482
+
483
+ #: classes/models/class-import.php:180
484
+ msgid "assign posts to an existing user:"
485
+ msgstr ""
486
+
487
+ #: classes/models/class-import.php:182
488
+ msgid "or assign posts to an existing user:"
489
+ msgstr ""
490
+
491
+ #: classes/models/class-import.php:183
492
+ #: templates/column/metabox-column-options.php:23
493
+ msgid "- Select -"
494
+ msgstr ""
495
+
496
+ #: classes/models/class-import.php:269
497
+ msgid "All done."
498
+ msgstr ""
499
+
500
+ #: classes/models/class-import.php:269
501
+ msgid "Have fun!"
502
+ msgstr ""
503
+
504
+ #: classes/models/class-import.php:270
505
+ msgid "Remember to update the passwords and roles of imported users."
506
+ msgstr ""
507
+
508
+ #: classes/models/class-import.php:286
509
+ msgid "Fetching attachments is not enabled"
510
+ msgstr ""
511
+
512
+ #: classes/models/class-import.php:299
513
+ msgid "Invalid file type"
514
+ msgstr ""
515
+
516
+ #: classes/models/class-import.php:379
517
+ msgid "Remote server did not respond"
518
+ msgstr ""
519
+
520
+ #: classes/models/class-import.php:385
521
+ #, php-format
522
+ msgid "Remote server returned error response %1$d %2$s"
523
+ msgstr ""
524
+
525
+ #: classes/models/class-import.php:394
526
+ msgid "Remote file is incorrect size"
527
+ msgstr ""
528
+
529
+ #: classes/models/class-import.php:399
530
+ msgid "Zero size file downloaded"
531
+ msgstr ""
532
+
533
+ #: classes/models/class-import.php:405
534
+ #, php-format
535
+ msgid "Remote file is too large, limit is %s"
536
+ msgstr ""
537
+
538
+ #: classes/models/class-import.php:422 classes/models/class-import.php:426
539
+ #: classes/models/class-import.php:436
540
+ msgid "Sorry, there has been an error."
541
+ msgstr ""
542
+
543
+ #: classes/models/class-import.php:427
544
+ #, php-format
545
+ msgid ""
546
+ "The export file could not be found at <code>%s</code>. It is likely that "
547
+ "this was caused by a permissions problem."
548
+ msgstr ""
549
+
550
+ #: classes/models/class-import.php:444
551
+ #, php-format
552
+ msgid ""
553
+ "This WXR file (version %s) may not be supported by this version of the "
554
+ "importer. Please consider updating."
555
+ msgstr ""
556
+
557
+ #: classes/models/class-import.php:501
558
+ #, php-format
559
+ msgid ""
560
+ "Failed to import author %s. Their posts will be attributed to the current "
561
+ "user."
562
+ msgstr ""
563
+
564
+ #: classes/models/class-import.php:558
565
+ #, php-format
566
+ msgid ""
567
+ "Failed to create new user for %s. Their posts will be attributed to the "
568
+ "current user."
569
+ msgstr ""
570
+
571
+ #: classes/models/class-import.php:609 classes/models/class-import.php:766
572
+ #, php-format
573
+ msgid "Failed to import %s %s"
574
+ msgstr ""
575
+
576
+ #: classes/models/class-import.php:656
577
+ #, php-format
578
+ msgid "Failed to import &#8220;%s&#8221;: Invalid post type %s"
579
+ msgstr ""
580
+
581
+ #: classes/models/class-import.php:673
582
+ #, php-format
583
+ msgid "%s &#8220;%s&#8221; already exists."
584
+ msgstr ""
585
+
586
+ #: classes/models/class-import.php:732
587
+ #, php-format
588
+ msgid "Failed to import %s &#8220;%s&#8221;"
589
+ msgstr ""
590
+
591
+ #: classes/modules/class-post.php:42
592
+ msgid "Column Type"
593
+ msgstr ""
594
+
595
+ #: classes/widgets/class-mp-timetable-widget.php:22
596
+ msgid "Display upcoming events."
597
+ msgstr ""
598
+
599
+ #: classes/widgets/class-mp-timetable-widget.php:24
600
+ msgid "Timetable Events"
601
+ msgstr ""
602
+
603
+ #: media/js/blocks/src/timetable/inspector.js:88 templates/popup/index.php:12
604
+ #: templates/popup/index.php:23 templates/popup/index.php:34
605
+ msgid "In order to display multiple points hold ctrl/cmd button."
606
+ msgstr ""
607
+
608
+ #: media/js/blocks/src/timetable/inspector.js:96
609
+ msgid "Specific events"
610
+ msgstr ""
611
+
612
+ #: media/js/blocks/src/timetable/inspector.js:132 templates/popup/index.php:44
613
+ msgid "Event Head"
614
+ msgstr ""
615
+
616
+ #: media/js/blocks/src/timetable/inspector.js:137 templates/popup/index.php:49
617
+ msgid "Block height in pixels"
618
+ msgstr ""
619
+
620
+ #: media/js/blocks/src/timetable/inspector.js:147 templates/popup/index.php:56
621
+ msgid "Base font size"
622
+ msgstr ""
623
+
624
+ #: media/js/blocks/src/timetable/inspector.js:148 templates/popup/index.php:59
625
+ msgid "Base font size for the table. Example 12px, 2em, 80%."
626
+ msgstr ""
627
+
628
+ #: media/js/blocks/src/timetable/inspector.js:158 templates/popup/index.php:63
629
+ msgid "Time frame for event"
630
+ msgstr ""
631
+
632
+ #: media/js/blocks/src/timetable/inspector.js:168 templates/popup/index.php:73
633
+ msgid "Filter events style"
634
+ msgstr ""
635
+
636
+ #: media/js/blocks/src/timetable/inspector.js:177 templates/popup/index.php:83
637
+ msgid "Filter title to display all events"
638
+ msgstr ""
639
+
640
+ #: media/js/blocks/src/timetable/inspector.js:182 templates/popup/index.php:89
641
+ msgid "Hide 'All Events' option"
642
+ msgstr ""
643
+
644
+ #: media/js/blocks/src/timetable/inspector.js:191 templates/popup/index.php:99
645
+ msgid "Hide column with hours"
646
+ msgstr ""
647
+
648
+ #: media/js/blocks/src/timetable/inspector.js:200 templates/popup/index.php:108
649
+ msgid "Do not display empty rows"
650
+ msgstr ""
651
+
652
+ #: media/js/blocks/src/timetable/inspector.js:209 templates/popup/index.php:117
653
+ msgid "Merge cells with common events"
654
+ msgstr ""
655
+
656
+ #: media/js/blocks/src/timetable/inspector.js:218 templates/popup/index.php:126
657
+ #: templates/widgets/gallery-list.php:56
658
+ msgid "Disable event link"
659
+ msgstr ""
660
+
661
+ #: media/js/blocks/src/timetable/inspector.js:227 templates/popup/index.php:135
662
+ msgid "Horizontal align"
663
+ msgstr ""
664
+
665
+ #: media/js/blocks/src/timetable/inspector.js:237 templates/popup/index.php:144
666
+ msgid "Vertical align"
667
+ msgstr ""
668
+
669
+ #: media/js/blocks/src/timetable/inspector.js:241
670
+ msgid "Default"
671
+ msgstr ""
672
+
673
+ #: media/js/blocks/src/timetable/inspector.js:242 templates/popup/index.php:147
674
+ msgid "top"
675
+ msgstr ""
676
+
677
+ #: media/js/blocks/src/timetable/inspector.js:243 templates/popup/index.php:148
678
+ msgid "middle"
679
+ msgstr ""
680
+
681
+ #: media/js/blocks/src/timetable/inspector.js:244 templates/popup/index.php:149
682
+ msgid "bottom"
683
+ msgstr ""
684
+
685
+ #: media/js/blocks/src/timetable/inspector.js:248 templates/popup/index.php:154
686
+ msgid "Unique ID"
687
+ msgstr ""
688
+
689
+ #: media/js/blocks/src/timetable/inspector.js:249 templates/popup/index.php:157
690
+ msgid ""
691
+ "If you use more than one table on a page specify the unique ID for a "
692
+ "timetable. It is usually all lowercase and contains only letters, numbers, "
693
+ "and hyphens."
694
+ msgstr ""
695
+
696
+ #: media/js/blocks/src/timetable/inspector.js:254 templates/popup/index.php:161
697
+ msgid "CSS class"
698
+ msgstr ""
699
+
700
+ #: media/js/blocks/src/timetable/inspector.js:259 templates/popup/index.php:167
701
+ msgid "Mobile behavior"
702
+ msgstr ""
703
+
704
+ #: media/js/blocks/src/timetable/inspector.js:263 templates/popup/index.php:171
705
+ msgid "Table"
706
+ msgstr ""
707
+
708
+ #: media/js/blocks/src/timetable/inspector.js:264 templates/popup/index.php:170
709
+ msgid "List"
710
+ msgstr ""
711
+
712
+ #: templates-functions/actions-mp-event-functions.php:27
713
+ #: templates/theme/event-timeslots.php:7
714
+ #, php-format
715
+ msgid "Event Timeslots (%s)"
716
+ msgstr ""
717
+
718
+ #: templates/column/metabox-column-options.php:11
719
+ msgid "Simple Column"
720
+ msgstr ""
721
+
722
+ #: templates/column/metabox-column-options.php:20
723
+ msgid "Day of the week"
724
+ msgstr ""
725
+
726
+ #: templates/column/metabox-column-options.php:24
727
+ msgid "Sunday"
728
+ msgstr ""
729
+
730
+ #: templates/column/metabox-column-options.php:25
731
+ msgid "Monday"
732
+ msgstr ""
733
+
734
+ #: templates/column/metabox-column-options.php:26
735
+ msgid "Tuesday"
736
+ msgstr ""
737
+
738
+ #: templates/column/metabox-column-options.php:27
739
+ msgid "Wednesday"
740
+ msgstr ""
741
+
742
+ #: templates/column/metabox-column-options.php:28
743
+ msgid "Thursday"
744
+ msgstr ""
745
+
746
+ #: templates/column/metabox-column-options.php:29
747
+ msgid "Friday"
748
+ msgstr ""
749
+
750
+ #: templates/column/metabox-column-options.php:30
751
+ msgid "Saturday"
752
+ msgstr ""
753
+
754
+ #: templates/column/metabox-column-options.php:39
755
+ msgid "Date"
756
+ msgstr ""
757
+
758
+ #: templates/events/event-data.php:5
759
+ msgid "Start"
760
+ msgstr ""
761
+
762
+ #: templates/events/event-data.php:6
763
+ msgid "End"
764
+ msgstr ""
765
+
766
+ #: templates/events/event-data.php:8
767
+ msgid "Head"
768
+ msgstr ""
769
+
770
+ #: templates/events/event-data.php:9
771
+ msgid "Actions"
772
+ msgstr ""
773
+
774
+ #: templates/events/event-data.php:32
775
+ msgid "Edit event in the form below"
776
+ msgstr ""
777
+
778
+ #: templates/events/event-data.php:33
779
+ msgid "Delete"
780
+ msgstr ""
781
+
782
+ #: templates/events/metabox-event-data.php:10
783
+ msgid "Column:"
784
+ msgstr ""
785
+
786
+ #: templates/events/metabox-event-data.php:18
787
+ #, php-format
788
+ msgid "Select column or <a target=\"_blank\" href=\"%s\">Add New</a>."
789
+ msgstr ""
790
+
791
+ #: templates/events/metabox-event-data.php:20
792
+ #, php-format
793
+ msgid "No columns found. <a href=\"%s\">Create at least one column first.</a>"
794
+ msgstr ""
795
+
796
+ #: templates/events/metabox-event-data.php:26
797
+ msgid "Start Time:"
798
+ msgstr ""
799
+
800
+ #: templates/events/metabox-event-data.php:29
801
+ #: templates/events/metabox-event-data.php:36
802
+ msgid "hh:mm"
803
+ msgstr ""
804
+
805
+ #: templates/events/metabox-event-data.php:33
806
+ msgid "End Time:"
807
+ msgstr ""
808
+
809
+ #: templates/events/metabox-event-data.php:40
810
+ msgid "Description:"
811
+ msgstr ""
812
+
813
+ #: templates/events/metabox-event-data.php:44
814
+ msgid "Event Head:"
815
+ msgstr ""
816
+
817
+ #: templates/events/metabox-event-data.php:52
818
+ msgid "none"
819
+ msgstr ""
820
+
821
+ #: templates/events/metabox-event-data.php:75
822
+ msgid "Add New"
823
+ msgstr ""
824
+
825
+ #: templates/events/metabox-event-options.php:4
826
+ msgid "Event Subtitle:"
827
+ msgstr ""
828
+
829
+ #: templates/events/metabox-event-options.php:8
830
+ msgid "Background Color:"
831
+ msgstr ""
832
+
833
+ #: templates/events/metabox-event-options.php:15
834
+ msgid "Background Hover Color:"
835
+ msgstr ""
836
+
837
+ #: templates/events/metabox-event-options.php:22
838
+ msgid "Text Color:"
839
+ msgstr ""
840
+
841
+ #: templates/events/metabox-event-options.php:29
842
+ msgid "Text Hover Color:"
843
+ msgstr ""
844
+
845
+ #: templates/events/metabox-event-options.php:36
846
+ msgid "Custom Event URL:"
847
+ msgstr ""
848
+
849
+ #: templates/events/metabox-event-options.php:42
850
+ msgid "Disable link to this event:"
851
+ msgstr ""
852
+
853
+ #: templates/popup/index.php:5
854
+ msgid "<b>Columns</b> (required)"
855
+ msgstr ""
856
+
857
+ #: templates/popup/index.php:16
858
+ msgid "Specific <b>events</b>"
859
+ msgstr ""
860
+
861
+ #: templates/popup/index.php:27
862
+ msgid "Event <b>categories</b>"
863
+ msgstr ""
864
+
865
+ #: templates/popup/index.php:38
866
+ msgid "Fields to display:"
867
+ msgstr ""
868
+
869
+ #: templates/popup/index.php:45
870
+ msgid "Check the event parameter(s) to be displayed in the timetable."
871
+ msgstr ""
872
+
873
+ #: templates/popup/index.php:52
874
+ msgid "Set height of the block"
875
+ msgstr ""
876
+
877
+ #: templates/popup/index.php:76
878
+ msgid "Dropdown"
879
+ msgstr ""
880
+
881
+ #: templates/popup/index.php:78
882
+ msgid "None"
883
+ msgstr ""
884
+
885
+ #: templates/popup/index.php:146
886
+ msgid "default"
887
+ msgstr ""
888
+
889
+ #: templates/popup/index.php:173
890
+ msgid ""
891
+ "Tick \"List\" to display events in a list view on mobile devices. Tick "
892
+ "\"Table\" to display events in a table."
893
+ msgstr ""
894
+
895
+ #: templates/popup/index.php:179
896
+ msgid "Add Timetable"
897
+ msgstr ""
898
+
899
+ #: templates/settings/general.php:2
900
+ msgid "General Settings"
901
+ msgstr ""
902
+
903
+ #: templates/settings/general.php:9
904
+ msgid "Template Mode"
905
+ msgstr ""
906
+
907
+ #: templates/settings/general.php:13
908
+ msgid "Theme Mode"
909
+ msgstr ""
910
+
911
+ #: templates/settings/general.php:14
912
+ msgid "Developer Mode"
913
+ msgstr ""
914
+
915
+ #: templates/settings/general.php:16
916
+ msgid ""
917
+ "Choose Theme Mode to display the content with the styles of your theme. "
918
+ "Choose Developer Mode to control appearance of the content with custom page "
919
+ "templates, actions and filters."
920
+ msgstr ""
921
+
922
+ #: templates/settings/general.php:16
923
+ msgid ""
924
+ "This option can't be changed if your theme is initially integrated with the "
925
+ "plugin."
926
+ msgstr ""
927
+
928
+ #: templates/settings/general.php:23
929
+ msgid "Permalink Settings"
930
+ msgstr ""
931
+
932
+ #: templates/settings/general.php:24
933
+ #, php-format
934
+ msgid ""
935
+ "Configure permalink settings in <a href=\"%s\">Settings > Permalinks</a>"
936
+ msgstr ""
937
+
938
+ #: templates/settings/general.php:29
939
+ msgid "Save"
940
+ msgstr ""
941
+
942
+ #: templates/shortcodes/empty-search-events.php:1
943
+ #: templates/theme/widget-upcoming-view.php:54
944
+ #: templates/widgets/widget-view.php:91
945
+ msgid "no events found"
946
+ msgstr ""
947
+
948
+ #: templates/templates-actions/action-sidebar.php:8
949
+ #: templates/widgets/gallery-list.php:14
950
+ msgid "Today upcoming events"
951
+ msgstr ""
952
+
953
+ #: templates/templates-actions/action-sidebar.php:13
954
+ #: templates/widgets/gallery-list.php:16
955
+ msgid "All upcoming events"
956
+ msgstr ""
957
+
958
+ #: templates/widgets/gallery-list.php:10
959
+ msgid "Events to display"
960
+ msgstr ""
961
+
962
+ #: templates/widgets/gallery-list.php:18
963
+ msgid "Ongoing events"
964
+ msgstr ""
965
+
966
+ #: templates/widgets/gallery-list.php:23
967
+ msgid "Input number of days"
968
+ msgstr ""
969
+
970
+ #: templates/widgets/gallery-list.php:28
971
+ msgid "day"
972
+ msgstr ""
973
+
974
+ #: templates/widgets/gallery-list.php:32
975
+ msgid "Event categories. Leave blank to display all."
976
+ msgstr ""
977
+
978
+ #: templates/widgets/gallery-list.php:49
979
+ msgid "Number of events to display"
980
+ msgstr ""
981
+
982
+ #: templates/widgets/gallery-list.php:67
983
+ msgid "Custom link for events"
984
+ msgstr ""
985
+
986
+ #: templates/widgets/gallery-list.php:75
987
+ msgid "Event background color"
988
+ msgstr ""
989
+
990
+ #: templates/widgets/gallery-list.php:85
991
+ msgid "Event background color on hover"
992
+ msgstr ""
993
+
994
+ #: templates/widgets/gallery-list.php:95
995
+ msgid "Event text color"
996
+ msgstr ""
997
+
998
+ #: templates/widgets/gallery-list.php:105
999
+ msgid "Event text color on hover"
1000
+ msgstr ""
1001
+
1002
+ #: templates/widgets/gallery-list.php:115
1003
+ msgid "Event border color"
1004
+ msgstr ""
1005
+
1006
+ #: templates/widgets/gallery-list.php:125
1007
+ msgid "Event border color on hover"
1008
+ msgstr ""
languages/po2json.txt ADDED
@@ -0,0 +1 @@
 
1
+ po2json .\languages\mp-timetable-ru_RU.po .\languages\mp-timetable-ru_RU-mptt-blocks-js.json -f jed
media/css/admin.css ADDED
@@ -0,0 +1 @@
 
1
+ #mp-event_data #add_event_table #event_end,#mp-event_data #add_event_table #event_start{min-width:100px}#mp-event_data .spinner.left{float:none}#mp-event_data .events-list-wrapper{max-height:360px;overflow-y:auto;overflow-x:hidden;margin-bottom:1em}#mp-event_data #events-list .active *{background-color:#F7FCFE}#mp-event_data #events-list .active td:first-child{border-left:4px solid #0091cd}#mp-event_data #events-list .delete-event-button,#mp-event_data #events-list .edit-event-button{margin:0 5px 10px 0;position:relative;width:2em;height:2em}#mp-event_data #events-list .delete-event-button.icon:before,#mp-event_data #events-list .edit-event-button.icon:before{font-family:Dashicons;font-variant:normal;font-weight:400;height:100%;left:0;line-height:1.85;margin:0;position:absolute;text-align:center;text-indent:0;text-transform:none;top:0;width:100%}@media screen and (max-width:1200px){#mp-event_data #events-list .event-description{overflow:hidden;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap}}#column-options .mp-date,#column-options .mp-weekday{margin:10px 0 0}div[id^=jBoxID].jBox-Modal .jBox-container{overflow:hidden}div[id^=jBoxID].jBox-Modal .jBox-content{max-width:980px;max-height:800px;padding:0;overflow:hidden}div[id^=jBoxID].jBox-Modal .jBox-content>form{height:100%;overflow:hidden;position:relative}div[id^=jBoxID].jBox-Modal .jBox-content>form .mptt-shortcode-settings-wrapper{height:calc(100% - 60px);overflow-y:scroll;padding:0 15px 0 10px}div[id^=jBoxID].jBox-Modal .jBox-content>form .mptt-shortcode-submit-wrapper{background:#f4f5f6;border-top:1px solid #ddd;padding:15px;position:relative;text-align:left}#add_event_options_table.form-table tr>:first-child,#add_event_table.form-table tr>:first-child{vertical-align:top;text-align:left;padding:20px 10px 20px 0;width:200px;line-height:1.3;font-weight:600}
media/css/block-editor.css ADDED
@@ -0,0 +1 @@
 
1
+ .wp-block[data-type="mp-timetable/timetable"] div[data-block]{overflow:auto}
media/css/images/column_icon.png ADDED
Binary file
media/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png ADDED
Binary file
media/css/images/ui-bg_diagonals-thick_20_666666_40x40.png ADDED
Binary file
media/css/images/ui-bg_flat_10_000000_40x100.png ADDED
Binary file
media/css/images/ui-bg_glass_100_f6f6f6_1x400.png ADDED
Binary file
media/css/images/ui-bg_glass_100_fdf5ce_1x400.png ADDED
Binary file
media/css/images/ui-bg_glass_65_ffffff_1x400.png ADDED
Binary file
media/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png ADDED
Binary file
media/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png ADDED
Binary file
media/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png ADDED
Binary file
media/css/images/ui-icons_222222_256x240.png ADDED
Binary file
media/css/images/ui-icons_228ef1_256x240.png ADDED
Binary file
media/css/images/ui-icons_ef8c08_256x240.png ADDED
Binary file
media/css/images/ui-icons_ffd27a_256x240.png ADDED
Binary file
media/css/images/ui-icons_ffffff_256x240.png ADDED
Binary file
media/css/jbox/jBox.css ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* Global */
3
+
4
+ .jBox-wrapper {
5
+ text-align: left;
6
+ }
7
+
8
+ .jBox-wrapper,
9
+ .jBox-wrapper * {
10
+ -webkit-box-sizing: border-box;
11
+ box-sizing: border-box;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ .jBox-title,
17
+ .jBox-content,
18
+ .jBox-container {
19
+ position: relative;
20
+ word-break: break-word;
21
+ }
22
+
23
+ .jBox-container {
24
+ background: #fff;
25
+ }
26
+
27
+ .jBox-content {
28
+ padding: 8px 10px;
29
+ overflow: auto;
30
+ -webkit-transition: opacity .15s;
31
+ transition: opacity .15s;
32
+ }
33
+
34
+ /* jBox Tooltip */
35
+
36
+ .jBox-Tooltip .jBox-container,
37
+ .jBox-Mouse .jBox-container {
38
+ border-radius: 3px;
39
+ box-shadow: 0 0 5px rgba(0, 0, 0, .3);
40
+ }
41
+
42
+ .jBox-Tooltip .jBox-title,
43
+ .jBox-Mouse .jBox-title {
44
+ padding: 8px 10px 0;
45
+ font-weight: bold;
46
+ }
47
+
48
+ .jBox-hasTitle.jBox-Tooltip .jBox-content,
49
+ .jBox-hasTitle.jBox-Mouse .jBox-content {
50
+ padding-top: 5px;
51
+ }
52
+
53
+ /* Pointer */
54
+
55
+ .jBox-pointer {
56
+ position: absolute;
57
+ overflow: hidden;
58
+ }
59
+
60
+ .jBox-pointer-top { top: 0; }
61
+ .jBox-pointer-bottom { bottom: 0; }
62
+ .jBox-pointer-left { left: 0; }
63
+ .jBox-pointer-right { right: 0; }
64
+
65
+ .jBox-pointer-top,
66
+ .jBox-pointer-bottom {
67
+ width: 30px;
68
+ height: 12px;
69
+ }
70
+
71
+ .jBox-pointer-left,
72
+ .jBox-pointer-right {
73
+ width: 12px;
74
+ height: 30px;
75
+ }
76
+
77
+ .jBox-pointer:after {
78
+ content: '';
79
+ width: 20px;
80
+ height: 20px;
81
+ position: absolute;
82
+ background: #fff;
83
+ -webkit-transform: rotate(45deg);
84
+ transform: rotate(45deg);
85
+ }
86
+
87
+ .jBox-pointer-top:after {
88
+ left: 5px;
89
+ top: 6px;
90
+ box-shadow: -1px -1px 4px rgba(0, 0, 0, .2);
91
+ }
92
+
93
+ .jBox-pointer-right:after {
94
+ top: 5px;
95
+ right: 6px;
96
+ box-shadow: 1px -1px 4px rgba(0, 0, 0, .2);
97
+ }
98
+
99
+ .jBox-pointer-bottom:after {
100
+ left: 5px;
101
+ bottom: 6px;
102
+ box-shadow: 1px 1px 4px rgba(0, 0, 0, .2);
103
+ }
104
+
105
+ .jBox-pointer-left:after {
106
+ top: 5px;
107
+ left: 6px;
108
+ box-shadow: -1px 1px 4px rgba(0, 0, 0, .2);
109
+ }
110
+
111
+ /* jBox Modal & jBox Confirm */
112
+
113
+ .jBox-Modal .jBox-container,
114
+ .jBox-Confirm .jBox-container {
115
+ border-radius: 3px;
116
+ box-shadow: 0 3px 15px rgba(0, 0, 0, .4), 0 0 5px rgba(0, 0, 0, .4);
117
+ }
118
+
119
+ .jBox-Modal .jBox-title,
120
+ .jBox-Confirm .jBox-title {
121
+ border-radius: 3px 3px 0 0;
122
+ padding: 10px 15px;
123
+ background: #f4f5f6;
124
+ border-bottom: 1px solid #ddd;
125
+ text-shadow: 0 1px 0 #fff;
126
+ }
127
+
128
+ .jBox-Modal.jBox-closeButton-title .jBox-title,
129
+ .jBox-Confirm.jBox-closeButton-title .jBox-title {
130
+ padding-right: 55px;
131
+ }
132
+
133
+ .jBox-Modal.jBox-closeButton-box:before,
134
+ .jBox-Confirm.jBox-closeButton-box:before {
135
+ box-shadow: 0 3px 15px rgba(0, 0, 0, .4), 0 0 5px rgba(0, 0, 0, .4);
136
+ }
137
+
138
+ /* jBox Modal */
139
+
140
+ .jBox-Modal .jBox-content {
141
+ padding: 12px 15px;
142
+ }
143
+
144
+ /* jBox Confirm */
145
+
146
+ .jBox-Confirm .jBox-content {
147
+ text-align: center;
148
+ padding: 45px 35px;
149
+ }
150
+
151
+ .jBox-Confirm-footer {
152
+ border-top: 1px solid #e2e2e2;
153
+ background: #fafafa;
154
+ border-radius: 0 0 3px 3px;
155
+ text-align: center;
156
+ padding: 10px 0;
157
+ }
158
+
159
+ .jBox-Confirm-button {
160
+ display: inline-block;
161
+ cursor: pointer;
162
+ font-size: 15px;
163
+ line-height: 30px;
164
+ height: 30px;
165
+ border-radius: 3px;
166
+ padding: 0 20px;
167
+ -webkit-transition: color .2s, background-color .2s;
168
+ transition: color .2s, background-color .2s;
169
+ }
170
+
171
+ .jBox-Confirm-button-cancel {
172
+ text-shadow: 0 1px 1px rgba(255, 255, 255, .6);
173
+ background: #ddd;
174
+ color: #999;
175
+ margin-right: 25px;
176
+ }
177
+
178
+ .jBox-Confirm-button-cancel:hover {
179
+ background: #ccc;
180
+ color: #666;
181
+ }
182
+
183
+ .jBox-Confirm-button-submit {
184
+ text-shadow: 0 -1px 1px rgba(0, 0, 0, .2);
185
+ background: #5fc04c;
186
+ color: #fff;
187
+ }
188
+
189
+ .jBox-Confirm-button-submit:hover {
190
+ background: #53a642;
191
+ }
192
+
193
+ .jBox-Confirm-button-cancel:active,
194
+ .jBox-Confirm-button-submit:active {
195
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, .26);
196
+ }
197
+
198
+ /* jBox Notice */
199
+
200
+ .jBox-Notice {
201
+ -webkit-transition: margin .2s;
202
+ transition: margin .2s;
203
+ }
204
+
205
+ .jBox-Notice .jBox-container {
206
+ border-radius: 3px;
207
+ box-shadow: 0 0 3px rgba(0, 0, 0, .2);
208
+ color: #fff;
209
+ text-shadow: 0 -1px 0 #000;
210
+ background: #333;
211
+ background-image: linear-gradient(to bottom, #444, #222);
212
+ }
213
+
214
+ .jBox-Notice .jBox-content {
215
+ border-radius: 3px;
216
+ padding: 12px 20px;
217
+ }
218
+
219
+ .jBox-Notice .jBox-title {
220
+ padding: 8px 20px 0;
221
+ font-weight: bold;
222
+ }
223
+
224
+ .jBox-hasTitle.jBox-Notice .jBox-content {
225
+ padding-top: 5px;
226
+ }
227
+
228
+ .jBox-Notice-color .jBox-container {
229
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, .3);
230
+ }
231
+
232
+ .jBox-Notice-gray .jBox-container {
233
+ color: #666;
234
+ text-shadow: 0 1px 0 #fff;
235
+ background: #f4f4f4;
236
+ background-image: linear-gradient(to bottom, #fafafa, #f0f0f0);
237
+ }
238
+
239
+ .jBox-Notice-red .jBox-container {
240
+ background: #b02222;
241
+ background-image: linear-gradient(to bottom, #ee2222, #b02222);
242
+ }
243
+
244
+ .jBox-Notice-green .jBox-container {
245
+ background: #70a800;
246
+ background-image: linear-gradient(to bottom, #95cc2a, #70a800);
247
+ }
248
+
249
+ .jBox-Notice-blue .jBox-container {
250
+ background: #2b91d9;
251
+ background-image: linear-gradient(to bottom, #5abaff, #2b91d9);
252
+ }
253
+
254
+ .jBox-Notice-yellow .jBox-container {
255
+ color: #744700;
256
+ text-shadow: 0 1px 0 rgba(255, 255, 255, .6);
257
+ background: #ffb11f;
258
+ background-image: linear-gradient(to bottom, #ffd665, #ffb11f);
259
+ }
260
+
261
+ /* jBox Image */
262
+
263
+ .jBox-Image {
264
+ background: #fff;
265
+ padding: 8px 8px 45px;
266
+ border-radius: 5px;
267
+ }
268
+
269
+ .jBox-Image .jBox-content {
270
+ padding: 0;
271
+ width: 100%;
272
+ height: 100%;
273
+ }
274
+
275
+ .jBox-image-container {
276
+ border-radius: 5px;
277
+ background: #fff center center no-repeat;
278
+ position: absolute;
279
+ width: 100%;
280
+ height: 100%;
281
+ opacity: 0;
282
+ }
283
+
284
+ .jBox-image-label {
285
+ box-sizing: border-box;
286
+ position: absolute;
287
+ background: #fff;
288
+ top: 100%;
289
+ left: 0;
290
+ width: 100%;
291
+ color: #333;
292
+ margin-top: -35px;
293
+ padding: 0 90px 5px 10px;
294
+ border-radius: 0 0 5px 5px;
295
+ -webkit-transition: opacity .3s;
296
+ transition: opacity .3s;
297
+ opacity: 0;
298
+ }
299
+
300
+ .jBox-image-label.active {
301
+ opacity: 1;
302
+ }
303
+
304
+ .jBox-image-pointer-next,
305
+ .jBox-image-pointer-prev {
306
+ position: absolute;
307
+ bottom: 0px;
308
+ width: 22px;
309
+ height: 45px;
310
+ background: no-repeat center center url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ijc0LjcgMjI0IDE4LjcgMzIiPg0KPHBhdGggZmlsbD0iIzAwMDAwMCIgZD0iTTkzLDIyNy40TDgwLjQsMjQwTDkzLDI1Mi42YzAuNCwwLjQsMC40LDEuMSwwLDEuNWwtMS42LDEuNmMtMC40LDAuNC0xLDAuNS0xLjUsMEw3NSwyNDAuN2MtMC40LTAuNC0wLjUtMSwwLTEuNWwxNC45LTE0LjljMC40LTAuNCwxLTAuNCwxLjUsMGwxLjYsMS42QzkzLjUsMjI2LjQsOTMuNCwyMjcsOTMsMjI3LjR6Ii8+DQo8L3N2Zz4=);
311
+ background-size: 11px auto;
312
+ cursor: pointer;
313
+ opacity: .6;
314
+ -webkit-touch-callout: none;
315
+ -webkit-user-select: none;
316
+ -moz-user-select: none;
317
+ -ms-user-select: none;
318
+ user-select: none;
319
+ -webkit-transition: opacity .2s;
320
+ transition: opacity .2s;
321
+ }
322
+
323
+ .jBox-image-pointer-next:hover,
324
+ .jBox-image-pointer-prev:hover {
325
+ opacity: 1;
326
+ }
327
+
328
+ .jBox-image-pointer-next {
329
+ right: 8px;
330
+ -webkit-transform: scaleX(-1);
331
+ transform: scaleX(-1);
332
+ }
333
+
334
+ .jBox-image-pointer-prev {
335
+ right: 30px;
336
+ }
337
+
338
+ .jBox-image-open #jBox-overlay {
339
+ background-color: rgba(0, 0, 0, .86);
340
+ }
341
+
342
+ .jBox-Image.jBox-loading .jBox-container:before {
343
+ left: auto;
344
+ top: auto;
345
+ bottom: -33px;
346
+ right: 55px;
347
+ margin-top: -9px;
348
+ margin-left: -9px;
349
+ }
350
+
351
+ /* Close button */
352
+
353
+ .jBox-closeButton {
354
+ cursor: pointer;
355
+ position: absolute;
356
+ }
357
+
358
+ .jBox-closeButton svg {
359
+ position: absolute;
360
+ top: 50%;
361
+ right: 50%;
362
+ }
363
+
364
+ .jBox-closeButton path {
365
+ -webkit-transition: fill .2s;
366
+ transition: fill .2s;
367
+ }
368
+
369
+ .jBox-closeButton path {
370
+ fill: #aaa;
371
+ }
372
+
373
+ .jBox-closeButton:hover path {
374
+ fill: #888;
375
+ }
376
+
377
+ .jBox-closeButton:active path {
378
+ fill: #666;
379
+ }
380
+
381
+ /* Close button in overlay */
382
+
383
+ #jBox-overlay .jBox-closeButton {
384
+ top: 0;
385
+ right: 0;
386
+ width: 40px;
387
+ height: 40px;
388
+ }
389
+
390
+ #jBox-overlay .jBox-closeButton svg {
391
+ width: 20px;
392
+ height: 20px;
393
+ margin-top: -10px;
394
+ margin-right: -10px;
395
+ }
396
+
397
+ #jBox-overlay .jBox-closeButton path {
398
+ fill: #d2d4d6;
399
+ }
400
+
401
+ #jBox-overlay .jBox-closeButton:hover path {
402
+ fill: #fff;
403
+ }
404
+
405
+ #jBox-overlay .jBox-closeButton:active path {
406
+ fill: #b2b4b6;
407
+ }
408
+
409
+ /* Close button in title */
410
+
411
+ .jBox-closeButton-title .jBox-closeButton {
412
+ top: 0;
413
+ right: 0;
414
+ bottom: 0;
415
+ width: 40px;
416
+ }
417
+
418
+ .jBox-closeButton-title .jBox-closeButton svg {
419
+ width: 12px;
420
+ height: 12px;
421
+ margin-top: -6px;
422
+ margin-right: -6px;
423
+ }
424
+
425
+ /* Close button in box */
426
+
427
+ .jBox-closeButton-box .jBox-closeButton {
428
+ top: -8px;
429
+ right: -10px;
430
+ width: 24px;
431
+ height: 24px;
432
+ background: #fff;
433
+ border-radius: 50%;
434
+ }
435
+
436
+ .jBox-closeButton-box .jBox-closeButton svg {
437
+ width: 10px;
438
+ height: 10px;
439
+ margin-top: -5px;
440
+ margin-right: -5px;
441
+ }
442
+
443
+ .jBox-hasTitle.jBox-Modal.jBox-closeButton-box .jBox-closeButton {
444
+ background: #f4f5f6;
445
+ }
446
+
447
+ .jBox-closeButton-box:before {
448
+ content: '';
449
+ position: absolute;
450
+ top: -8px;
451
+ right: -10px;
452
+ width: 24px;
453
+ height: 24px;
454
+ border-radius: 50%;
455
+ box-shadow: 0 0 5px rgba(0, 0, 0, .3);
456
+ }
457
+
458
+ .jBox-pointerPosition-top.jBox-closeButton-box:before {
459
+ top: 4px;
460
+ }
461
+
462
+ .jBox-pointerPosition-right.jBox-closeButton-box:before {
463
+ right: 2px;
464
+ }
465
+
466
+ /* Overlay */
467
+
468
+ #jBox-overlay {
469
+ position: fixed;
470
+ top: 0;
471
+ left: 0;
472
+ width: 100%;
473
+ height: 100%;
474
+ background: #000;
475
+ background-color: rgba(0, 0, 0, .6);
476
+ }
477
+
478
+ /* Block scrolling */
479
+
480
+ body[class^="jBox-blockScroll-"],
481
+ body[class*=" jBox-blockScroll-"] {
482
+ overflow: hidden;
483
+ }
484
+
485
+ /* Draggable */
486
+
487
+ .jBox-draggable {
488
+ cursor: move;
489
+ }
490
+
491
+ /* Spinner */
492
+
493
+ @keyframes jBoxLoading {
494
+ to {transform: rotate(360deg);}
495
+ }
496
+
497
+ @-webkit-keyframes jBoxLoading {
498
+ to {-webkit-transform: rotate(360deg);}
499
+ }
500
+
501
+ .jBox-loading .jBox-content {
502
+ min-height: 32px;
503
+ min-width: 38px;
504
+ opacity: 0;
505
+ }
506
+
507
+ .jBox-spinner {
508
+ position: absolute;
509
+ top: 50%;
510
+ left: 50%;
511
+ width: 20px;
512
+ height: 20px;
513
+ margin-top: -10px;
514
+ margin-left: -10px;
515
+ }
516
+
517
+ .jBox-spinner:before {
518
+ content: 'Loading…';
519
+ display: block;
520
+ width: 20px;
521
+ height: 20px;
522
+ text-align: center;
523
+ -webkit-box-sizing: border-box;
524
+ box-sizing: border-box;
525
+ }
526
+
527
+ .jBox-spinner:not(:required):before {
528
+ content: '';
529
+ border-radius: 50%;
530
+ border: 2px solid rgba(0, 0, 0, .3);
531
+ border-top-color: rgba(0, 0, 0, .6);
532
+ animation: jBoxLoading .6s linear infinite;
533
+ -webkit-animation: jBoxLoading .6s linear infinite;
534
+ }
535
+
536
+ /* IE8 fixes */
537
+
538
+ .jBox-IE8.jBox-Tooltip .jBox-container,
539
+ .jBox-IE8.jBox-Mouse .jBox-container {
540
+ border: 1px solid #aaa;
541
+ }
542
+
543
+ .jBox-IE8 .jBox-pointer:after {
544
+ display: none;
545
+ }
546
+
547
+ .jBox-IE8 .jBox-pointer {
548
+ border: 0;
549
+ background: no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNq01l0OwyAIAGAlvY+n8ZJ6Gk/EqqkNtf7ApCQ+LM34iuCmRUQzihjj6FH+kjWL8N4/Ph9GHpiTnC9SwDbhLGyvspSScc71KkOa/HpuuRhIK+psE2pjONouCQg7kBSEXUgC2tHo52mTTBpnaEATWlaYK6MrhIAaceWpOcsCrYp6FV4H/90zTWjUQ/gSevVQq0ecHqoOxWpYoO7p5O9ku2fnVtp7QAik2rsK3fnpWfjynJWpbw+1BkghurrYDjiCptg/4AxaYhJwBbEwDsiB2NgM5EIirAdKIDFGQSmU1+NaIPjJYt2I25vxT4ABAMhWvtle2YvmAAAAAElFTkSuQmCC);
550
+ }
551
+
552
+ .jBox-IE8 .jBox-pointer-top { background-position: center top; }
553
+ .jBox-IE8 .jBox-pointer-bottom { background-position: center bottom; }
554
+ .jBox-IE8 .jBox-pointer-left { background-position: left center; }
555
+ .jBox-IE8 .jBox-pointer-right { background-position: right center; }
556
+
557
+ .jBox-IE8.jBox-Modal .jBox-container {
558
+ border: 3px solid #aaa;
559
+ }
560
+
561
+ /* No SVG support fixes */
562
+
563
+ .jBox-nosvg .jBox-closeButton:before {
564
+ font-family: Verdana, sans-serif;
565
+ content: 'x';
566
+ text-align: center;
567
+ font-size: 18px;
568
+ color: #888;
569
+ }
media/css/jbox/themes/ModalBorder.css ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* Wrapper */
3
+
4
+ .jBox-ModalBorder {
5
+ border-radius: 8px;
6
+ background: rgba(0, 0, 0, .4);
7
+ padding: 8px;
8
+ box-shadow: 0 0 6px rgba(0, 0, 0, .2);
9
+ }
10
+
11
+ /* Container */
12
+
13
+ .jBox-ModalBorder .jBox-container {
14
+ border-radius: 5px;
15
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
16
+ }
17
+
18
+ /* Close button */
19
+
20
+ .jBox-ModalBorder.jBox-closeButton-box {
21
+ border-top-right-radius: 0;
22
+ }
23
+
24
+ .jBox-ModalBorder.jBox-closeButton-box:before {
25
+ display: none;
26
+ }
27
+
28
+ .jBox-ModalBorder.jBox-hasTitle.jBox-closeButton-box .jBox-closeButton,
29
+ .jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton {
30
+ background: rgba(0, 0, 0, .4);
31
+ border-radius: 0 50% 50% 0;
32
+ right: -32px;
33
+ top: -8px;
34
+ }
35
+
36
+ .jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton path {
37
+ fill: #d2d4d6;
38
+ }
39
+
40
+ .jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton:hover path {
41
+ fill: #fff;
42
+ }
43
+
44
+ .jBox-ModalBorder.jBox-closeButton-box .jBox-closeButton:active path {
45
+ fill: #b2b4b6;
46
+ }
media/css/jbox/themes/NoticeBorder.css ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* jBox: Notice */
3
+
4
+ .jBox-NoticeBorder .jBox-container {
5
+ border-radius: 6px;
6
+ }
7
+
8
+ .jBox-NoticeBorder .jBox-content,
9
+ .jBox-NoticeBorder .jBox-title {
10
+ padding-left: 26px;
11
+ }
12
+
13
+ .jBox-NoticeBorder.jBox-Notice-color .jBox-container {
14
+ color: #fff;
15
+ text-shadow: 0 -1px 0 #000;
16
+ background: rgba(0, 0, 0, .92);
17
+ }
18
+
19
+ .jBox-NoticeBorder.jBox-Notice-color .jBox-container:after {
20
+ content: '';
21
+ position: absolute;
22
+ top: 0;
23
+ left: 0;
24
+ bottom: 0;
25
+ width: 8px;
26
+ border-radius: 5px 0 0 5px;
27
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, .5) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .5) 50%, rgba(255, 255, 255, .5) 75%, transparent 75%, transparent);
28
+ background-size: 18px 18px;
29
+ }
30
+
31
+ .jBox-NoticeBorder.jBox-Notice-red .jBox-container:after {
32
+ background-color: #ee0000;
33
+ }
34
+
35
+ .jBox-NoticeBorder.jBox-Notice-green .jBox-container:after {
36
+ background-color: #95cc2a;
37
+ }
38
+
39
+ .jBox-NoticeBorder.jBox-Notice-blue .jBox-container:after {
40
+ background-color: #4cb4ff;
41
+ }
42
+
43
+ .jBox-NoticeBorder.jBox-Notice-yellow .jBox-container:after {
44
+ background-color: #ffba00;
45
+ }
media/css/jbox/themes/TooltipBorder.css ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* Container */
3
+
4
+ .jBox-TooltipBorder .jBox-container {
5
+ border-radius: 5px;
6
+ border: 2px solid #52a2cb;
7
+ }
8
+
9
+ /* Pointer */
10
+
11
+ .jBox-TooltipBorder .jBox-pointer:after {
12
+ border: 2px solid #52a2cb;
13
+ }
14
+
15
+ .jBox-TooltipBorder .jBox-pointer-top,
16
+ .jBox-TooltipBorder .jBox-pointer-bottom {
17
+ width: 34px;
18
+ height: 12px;
19
+ }
20
+
21
+ .jBox-TooltipBorder .jBox-pointer-left,
22
+ .jBox-TooltipBorder .jBox-pointer-right {
23
+ width: 12px;
24
+ height: 34px;
25
+ }
26
+
27
+ /* Close button */
28
+
29
+ .jBox-TooltipBorder.jBox-closeButton-box:before {
30
+ width: 28px;
31
+ height: 28px;
32
+ background: #52a2cb;
33
+ }
media/css/jbox/themes/TooltipDark.css ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* Container */
3
+
4
+ .jBox-TooltipDark .jBox-container {
5
+ border-radius: 3px;
6
+ background: #222;
7
+ color: #fff;
8
+ box-shadow: 0 0 6px rgba(0, 0, 0, .4);
9
+ }
10
+
11
+ /* Pointer */
12
+
13
+ .jBox-TooltipDark .jBox-pointer:after {
14
+ background: #222;
15
+ }
16
+
17
+ /* Close button */
18
+
19
+ .jBox-TooltipDark .jBox-closeButton {
20
+ background: #222;
21
+ }
22
+
23
+ .jBox-TooltipDark.jBox-closeButton-box:before {
24
+ box-shadow: 0 0 6px rgba(0, 0, 0, .4);
25
+ }
26
+
27
+ .jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton path {
28
+ fill: #d2d4d6;
29
+ }
30
+
31
+ .jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:hover path {
32
+ fill: #fff;
33
+ }
34
+
35
+ .jBox-TooltipDark.jBox-closeButton-box .jBox-closeButton:active path {
36
+ fill: #b2b4b6;
37
+ }
media/css/jquery-ui-1.10.0.custom.min.css ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /*! jQuery UI - v1.10.0 - 2013-01-25
2
+ * http://jqueryui.com
3
+ * Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css
4
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
5
+ * Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:21px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("images/animated-overlay.gif");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-tabs-loading a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px;background-position:16px 16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_ffffff_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_ef8c08_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_ef8c08_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_ef8c08_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_228ef1_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_ffd27a_256x240.png)}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);border-radius:5px}
media/css/jquery.ui.timepicker.css ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Timepicker stylesheet
3
+ * Highly inspired from datepicker
4
+ * FG - Nov 2010 - Web3R
5
+ *
6
+ * version 0.0.3 : Fixed some settings, more dynamic
7
+ * version 0.0.4 : Removed width:100% on tables
8
+ * version 0.1.1 : set width 0 on tables to fix an ie6 bug
9
+ */
10
+
11
+ .ui-timepicker-inline { display: inline; }
12
+
13
+ #ui-timepicker-div { padding: 0.2em; }
14
+ .ui-timepicker-table { display: inline-table; width: 0; }
15
+ .ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
16
+
17
+ .ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
18
+
19
+ .ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
20
+ .ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
21
+ .ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
22
+
23
+ /* span for disabled cells */
24
+ .ui-timepicker-table td span {
25
+ display:block;
26
+ padding:0.2em 0.3em 0.2em 0.5em;
27
+ width: 1.2em;
28
+
29
+ text-align:right;
30
+ text-decoration:none;
31
+ }
32
+ /* anchors for clickable cells */
33
+ .ui-timepicker-table td a {
34
+ display:block;
35
+ padding:0.2em 0.3em 0.2em 0.5em;
36
+ width: 1.2em;
37
+ cursor: pointer;
38
+ text-align:right;
39
+ text-decoration:none;
40
+ }
41
+
42
+
43
+ /* buttons and button pane styling */
44
+ .ui-timepicker .ui-timepicker-buttonpane {
45
+ background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
46
+ }
47
+ .ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
48
+ /* The close button */
49
+ .ui-timepicker .ui-timepicker-close { float: right }
50
+
51
+ /* the now button */
52
+ .ui-timepicker .ui-timepicker-now { float: left; }
53
+
54
+ /* the deselect button */
55
+ .ui-timepicker .ui-timepicker-deselect { float: left; }
56
+
57
+
media/css/spectrum.css ADDED
@@ -0,0 +1,507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /***
2
+ Spectrum Colorpicker v1.8.0
3
+ https://github.com/bgrins/spectrum
4
+ Author: Brian Grinstead
5
+ License: MIT
6
+ ***/
7
+
8
+ .sp-container {
9
+ position:absolute;
10
+ top:0;
11
+ left:0;
12
+ display:inline-block;
13
+ *display: inline;
14
+ *zoom: 1;
15
+ /* https://github.com/bgrins/spectrum/issues/40 */
16
+ z-index: 9999994;
17
+ overflow: hidden;
18
+ }
19
+ .sp-container.sp-flat {
20
+ position: relative;
21
+ }
22
+
23
+ /* Fix for * { box-sizing: border-box; } */
24
+ .sp-container,
25
+ .sp-container * {
26
+ -webkit-box-sizing: content-box;
27
+ -moz-box-sizing: content-box;
28
+ box-sizing: content-box;
29
+ }
30
+
31
+ /* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
32
+ .sp-top {
33
+ position:relative;
34
+ width: 100%;
35
+ display:inline-block;
36
+ }
37
+ .sp-top-inner {
38
+ position:absolute;
39
+ top:0;
40
+ left:0;
41
+ bottom:0;
42
+ right:0;
43
+ }
44
+ .sp-color {
45
+ position: absolute;
46
+ top:0;
47
+ left:0;
48
+ bottom:0;
49
+ right:20%;
50
+ }
51
+ .sp-hue {
52
+ position: absolute;
53
+ top:0;
54
+ right:0;
55
+ bottom:0;
56
+ left:84%;
57
+ height: 100%;
58
+ }
59
+
60
+ .sp-clear-enabled .sp-hue {
61
+ top:33px;
62
+ height: 77.5%;
63
+ }
64
+
65
+ .sp-fill {
66
+ padding-top: 80%;
67
+ }
68
+ .sp-sat, .sp-val {
69
+ position: absolute;
70
+ top:0;
71
+ left:0;
72
+ right:0;
73
+ bottom:0;
74
+ }
75
+
76
+ .sp-alpha-enabled .sp-top {
77
+ margin-bottom: 18px;
78
+ }
79
+ .sp-alpha-enabled .sp-alpha {
80
+ display: block;
81
+ }
82
+ .sp-alpha-handle {
83
+ position:absolute;
84
+ top:-4px;
85
+ bottom: -4px;
86
+ width: 6px;
87
+ left: 50%;
88
+ cursor: pointer;
89
+ border: 1px solid black;
90
+ background: white;
91
+ opacity: .8;
92
+ }
93
+ .sp-alpha {
94
+ display: none;
95
+ position: absolute;
96
+ bottom: -14px;
97
+ right: 0;
98
+ left: 0;
99
+ height: 8px;
100
+ }
101
+ .sp-alpha-inner {
102
+ border: solid 1px #333;
103
+ }
104
+
105
+ .sp-clear {
106
+ display: none;
107
+ }
108
+
109
+ .sp-clear.sp-clear-display {
110
+ background-position: center;
111
+ }
112
+
113
+ .sp-clear-enabled .sp-clear {
114
+ display: block;
115
+ position:absolute;
116
+ top:0px;
117
+ right:0;
118
+ bottom:0;
119
+ left:84%;
120
+ height: 28px;
121
+ }
122
+
123
+ /* Don't allow text selection */
124
+ .sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
125
+ -webkit-user-select:none;
126
+ -moz-user-select: -moz-none;
127
+ -o-user-select:none;
128
+ user-select: none;
129
+ }
130
+
131
+ .sp-container.sp-input-disabled .sp-input-container {
132
+ display: none;
133
+ }
134
+ .sp-container.sp-buttons-disabled .sp-button-container {
135
+ display: none;
136
+ }
137
+ .sp-container.sp-palette-buttons-disabled .sp-palette-button-container {
138
+ display: none;
139
+ }
140
+ .sp-palette-only .sp-picker-container {
141
+ display: none;
142
+ }
143
+ .sp-palette-disabled .sp-palette-container {
144
+ display: none;
145
+ }
146
+
147
+ .sp-initial-disabled .sp-initial {
148
+ display: none;
149
+ }
150
+
151
+
152
+ /* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
153
+ .sp-sat {
154
+ background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
155
+ background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
156
+ background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
157
+ background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
158
+ background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
159
+ background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
160
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
161
+ filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
162
+ }
163
+ .sp-val {
164
+ background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
165
+ background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
166
+ background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
167
+ background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
168
+ background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
169
+ background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
170
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
171
+ filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
172
+ }
173
+
174
+ .sp-hue {
175
+ background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
176
+ background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
177
+ background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
178
+ background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
179
+ background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
180
+ background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
181
+ }
182
+
183
+ /* IE filters do not support multiple color stops.
184
+ Generate 6 divs, line them up, and do two color gradients for each.
185
+ Yes, really.
186
+ */
187
+ .sp-1 {
188
+ height:17%;
189
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
190
+ }
191
+ .sp-2 {
192
+ height:16%;
193
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
194
+ }
195
+ .sp-3 {
196
+ height:17%;
197
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
198
+ }
199
+ .sp-4 {
200
+ height:17%;
201
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
202
+ }
203
+ .sp-5 {
204
+ height:16%;
205
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
206
+ }
207
+ .sp-6 {
208
+ height:17%;
209
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
210
+ }
211
+
212
+ .sp-hidden {
213
+ display: none !important;
214
+ }
215
+
216
+ /* Clearfix hack */
217
+ .sp-cf:before, .sp-cf:after { content: ""; display: table; }
218
+ .sp-cf:after { clear: both; }
219
+ .sp-cf { *zoom: 1; }
220
+
221
+ /* Mobile devices, make hue slider bigger so it is easier to slide */
222
+ @media (max-device-width: 480px) {
223
+ .sp-color { right: 40%; }
224
+ .sp-hue { left: 63%; }
225
+ .sp-fill { padding-top: 60%; }
226
+ }
227
+ .sp-dragger {
228
+ border-radius: 5px;
229
+ height: 5px;
230
+ width: 5px;
231
+ border: 1px solid #fff;
232
+ background: #000;
233
+ cursor: pointer;
234
+ position:absolute;
235
+ top:0;
236
+ left: 0;
237
+ }
238
+ .sp-slider {
239
+ position: absolute;
240
+ top:0;
241
+ cursor:pointer;
242
+ height: 3px;
243
+ left: -1px;
244
+ right: -1px;
245
+ border: 1px solid #000;
246
+ background: white;
247
+ opacity: .8;
248
+ }
249
+
250
+ /*
251
+ Theme authors:
252
+ Here are the basic themeable display options (colors, fonts, global widths).
253
+ See http://bgrins.github.io/spectrum/themes/ for instructions.
254
+ */
255
+
256
+ .sp-container {
257
+ border-radius: 0;
258
+ background-color: #ECECEC;
259
+ border: solid 1px #f0c49B;
260
+ padding: 0;
261
+ }
262
+ .sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear {
263
+ font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
264
+ -webkit-box-sizing: border-box;
265
+ -moz-box-sizing: border-box;
266
+ -ms-box-sizing: border-box;
267
+ box-sizing: border-box;
268
+ }
269
+ .sp-top {
270
+ margin-bottom: 3px;
271
+ }
272
+ .sp-color, .sp-hue, .sp-clear {
273
+ border: solid 1px #666;
274
+ }
275
+
276
+ /* Input */
277
+ .sp-input-container {
278
+ float:right;
279
+ width: 100px;
280
+ margin-bottom: 4px;
281
+ }
282
+ .sp-initial-disabled .sp-input-container {
283
+ width: 100%;
284
+ }
285
+ .sp-input {
286
+ font-size: 12px !important;
287
+ border: 1px inset;
288
+ padding: 4px 5px;
289
+ margin: 0;
290
+ width: 100%;
291
+ background:transparent;
292
+ border-radius: 3px;
293
+ color: #222;
294
+ }
295
+ .sp-input:focus {
296
+ border: 1px solid orange;
297
+ }
298
+ .sp-input.sp-validation-error {
299
+ border: 1px solid red;
300
+ background: #fdd;
301
+ }
302
+ .sp-picker-container , .sp-palette-container {
303
+ float:left;
304
+ position: relative;
305
+ padding: 10px;
306
+ padding-bottom: 300px;
307
+ margin-bottom: -290px;
308
+ }
309
+ .sp-picker-container {
310
+ width: 172px;
311
+ border-left: solid 1px #fff;
312
+ }
313
+
314
+ /* Palettes */
315
+ .sp-palette-container {
316
+ border-right: solid 1px #ccc;
317
+ }
318
+
319
+ .sp-palette-only .sp-palette-container {
320
+ border: 0;
321
+ }
322
+
323
+ .sp-palette .sp-thumb-el {
324
+ display: block;
325
+ position:relative;
326
+ float:left;
327
+ width: 24px;
328
+ height: 15px;
329
+ margin: 3px;
330
+ cursor: pointer;
331
+ border:solid 2px transparent;
332
+ }
333
+ .sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
334
+ border-color: orange;
335
+ }
336
+ .sp-thumb-el {
337
+ position:relative;
338
+ }
339
+
340
+ /* Initial */
341
+ .sp-initial {
342
+ float: left;
343
+ border: solid 1px #333;
344
+ }
345
+ .sp-initial span {
346
+ width: 30px;
347
+ height: 25px;
348
+ border:none;
349
+ display:block;
350
+ float:left;
351
+ margin:0;
352
+ }
353
+
354
+ .sp-initial .sp-clear-display {
355
+ background-position: center;
356
+ }
357
+
358
+ /* Buttons */
359
+ .sp-palette-button-container,
360
+ .sp-button-container {
361
+ float: right;
362
+ }
363
+
364
+ /* Replacer (the little preview div that shows up instead of the <input>) */
365
+ .sp-replacer {
366
+ margin:0;
367
+ overflow:hidden;
368
+ cursor:pointer;
369
+ padding: 4px;
370
+ display:inline-block;
371
+ *zoom: 1;
372
+ *display: inline;
373
+ border: solid 1px #91765d;
374
+ background: #eee;
375
+ color: #333;
376
+ vertical-align: middle;
377
+ }
378
+ .sp-replacer:hover, .sp-replacer.sp-active {
379
+ border-color: #F0C49B;
380
+ color: #111;
381
+ }
382
+ .sp-replacer.sp-disabled {
383
+ cursor:default;
384
+ border-color: silver;
385
+ color: silver;
386
+ }
387
+ .sp-dd {
388
+ padding: 2px 0;
389
+ height: 16px;
390
+ line-height: 16px;
391
+ float:left;
392
+ font-size:10px;
393
+ }
394
+ .sp-preview {
395
+ position:relative;
396
+ width:25px;
397
+ height: 20px;
398
+ border: solid 1px #222;
399
+ margin-right: 5px;
400
+ float:left;
401
+ z-index: 0;
402
+ }
403
+
404
+ .sp-palette {
405
+ *width: 220px;
406
+ max-width: 220px;
407
+ }
408
+ .sp-palette .sp-thumb-el {
409
+ width:16px;
410
+ height: 16px;
411
+ margin:2px 1px;
412
+ border: solid 1px #d0d0d0;
413
+ }
414
+
415
+ .sp-container {
416
+ padding-bottom:0;
417
+ }
418
+
419
+
420
+ /* Buttons: http://hellohappy.org/css3-buttons/ */
421
+ .sp-container button {
422
+ background-color: #eeeeee;
423
+ background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
424
+ background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
425
+ background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
426
+ background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
427
+ background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
428
+ border: 1px solid #ccc;
429
+ border-bottom: 1px solid #bbb;
430
+ border-radius: 3px;
431
+ color: #333;
432
+ font-size: 14px;
433
+ line-height: 1;
434
+ padding: 5px 4px;
435
+ text-align: center;
436
+ text-shadow: 0 1px 0 #eee;
437
+ vertical-align: middle;
438
+ }
439
+ .sp-container button:hover {
440
+ background-color: #dddddd;
441
+ background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
442
+ background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
443
+ background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
444
+ background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
445
+ background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
446
+ border: 1px solid #bbb;
447
+ border-bottom: 1px solid #999;
448
+ cursor: pointer;
449
+ text-shadow: 0 1px 0 #ddd;
450
+ }
451
+ .sp-container button:active {
452
+ border: 1px solid #aaa;
453
+ border-bottom: 1px solid #888;
454
+ -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
455
+ -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
456
+ -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
457
+ -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
458
+ box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
459
+ }
460
+ .sp-cancel {
461
+ font-size: 11px;
462
+ color: #d93f3f !important;
463
+ margin:0;
464
+ padding:2px;
465
+ margin-right: 5px;
466
+ vertical-align: middle;
467
+ text-decoration:none;
468
+
469
+ }
470
+ .sp-cancel:hover {
471
+ color: #d93f3f !important;
472
+ text-decoration: underline;
473
+ }
474
+
475
+
476
+ .sp-palette span:hover, .sp-palette span.sp-thumb-active {
477
+ border-color: #000;
478
+ }
479
+
480
+ .sp-preview, .sp-alpha, .sp-thumb-el {
481
+ position:relative;
482
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
483
+ }
484
+ .sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner {
485
+ display:block;
486
+ position:absolute;
487
+ top:0;left:0;bottom:0;right:0;
488
+ }
489
+
490
+ .sp-palette .sp-thumb-inner {
491
+ background-position: 50% 50%;
492
+ background-repeat: no-repeat;
493
+ }
494
+
495
+ .sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner {
496
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
497
+ }
498
+
499
+ .sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner {
500
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
501
+ }
502
+
503
+ .sp-clear-display {
504
+ background-repeat:no-repeat;
505
+ background-position: center;
506
+ background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==);
507
+ }
media/css/style.css ADDED
@@ -0,0 +1 @@
 
1
+ .mptt-main-wrapper .mptt-column.events-list .event .event-user .avatar,.mptt-main-wrapper .mptt-event.events-list .event .event-user .avatar{border-radius:50%}.twentyfourteen .tfmp{padding:12px 10px 0;max-width:474px;margin:0 auto}.twentyfourteen .tfmp div.product.hentry.has-post-thumbnail{margin-top:0}.twentythirteen h1{margin:0}.twentythirteen ul{padding:0}@media screen and (min-width:673px){.twentyfourteen .tfmp{padding-right:30px;padding-left:30px}}@media screen and (min-width:1040px){.twentyfourteen .tfmp{padding-right:15px;padding-left:15px}}@media screen and (min-width:1110px){.twentyfourteen .tfmp{padding-right:30px;padding-left:30px}}@media screen and (min-width:1218px){.twentyfourteen .tfmp{margin-right:54px}.full-width .twentyfourteen .tfmp{margin-right:auto}}.twentyfifteen .t15mp{padding-left:7.6923%;padding-right:7.6923%;padding-top:7.6923%;margin-bottom:7.6923%;background:#fff;-webkit-box-shadow:0 0 1px rgba(0,0,0,.15);box-shadow:0 0 1px rgba(0,0,0,.15)}.twentyfifteen .mptt-sidebar .widget.mptt-container{padding:0}@media screen and (min-width:38.75em){.twentyfifteen .t15mp{margin-right:7.6923%;margin-left:7.6923%;margin-top:8.3333%}}@media screen and (min-width:59.6875em){.twentyfifteen .t15mp{margin-left:8.3333%;margin-right:8.3333%;padding:10%}.single-product .twentyfifteen .entry-summary{padding:0!important}}.twentysixteen .site-main{margin-right:7.6923%;margin-left:7.6923%}.twentysixteen .entry-summary{margin-right:0;margin-left:0}@media screen and (min-width:44.375em){.twentysixteen .site-main{margin-right:23.0769%}}@media screen and (min-width:56.875em){.twentysixteen .site-main{margin-right:0;margin-left:0}}.mptt-column .event[id^=event_columns_],.mptt-theme-mode-event.event{overflow:auto}.mptt-main-wrapper.mp-event-item img:not(.avatar ){width:100%;height:auto}.mptt-main-wrapper .mptt-content{width:66.999%;margin-right:3%;float:left}.mptt-main-wrapper .mptt-sidebar{width:30%;float:left}.mptt-main-wrapper .event-title{margin:0 0 2rem}.mptt-main-wrapper .thumbnail-wrapper{margin-bottom:1rem}.mptt-main-wrapper .event-content,.mptt-main-wrapper .timeslots-title{margin:1rem 0}.mptt-main-wrapper .mptt-event.events-list{margin:0 0 1rem 2rem;list-style:none;padding:0}.mptt-main-wrapper .mptt-event.events-list .event{list-style:outside;padding:1rem}.mptt-main-wrapper .mptt-event.events-list .event .event-subtitle,.mptt-main-wrapper .mptt-event.events-list .event .event-title,.mptt-main-wrapper .mptt-event.events-list .event .event-user,.mptt-main-wrapper .mptt-event.events-list .event .timeslot{margin:.25rem 0 0;padding:0}.mptt-main-wrapper .mptt-event.events-list .event .event-description{margin:.25rem 0 0;padding:0;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.mptt-main-wrapper .mptt-event.events-list .event:last-child{margin-bottom:0}.mptt-main-wrapper .column-title{margin:0 0 2rem}.mptt-main-wrapper .column-content{margin:1rem 0}.mptt-main-wrapper .mptt-column.events-list{margin:0 0 2rem;list-style:none;padding:0}.mptt-main-wrapper .mptt-column.events-list .event{list-style:none;padding:1rem}.mptt-main-wrapper .mptt-column.events-list .event .event-thumbnail{float:left;margin:0 2rem 2rem 0}.mptt-main-wrapper .mptt-column.events-list .event .event-title{clear:none;margin:0 0 .25rem;padding:0}.mptt-main-wrapper .mptt-column.events-list .event .event-subtitle,.mptt-main-wrapper .mptt-column.events-list .event .event-user,.mptt-main-wrapper .mptt-column.events-list .event .timeslot{margin:.25rem 0 0;padding:0}.mptt-main-wrapper .mptt-column.events-list .event .event-description{margin:.25rem 0 0;padding:0;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.upcoming-events-widget{margin-top:2rem}.upcoming-events-widget .mptt-widget.events-list{list-style:none;margin:0;padding:0}.upcoming-events-widget .mptt-widget.events-list .event{list-style:none;margin-bottom:2rem;padding:.5rem 2rem;border:none;border-left:3px solid #3b5998}.upcoming-events-widget .mptt-widget.events-list .event .event-title{margin:0 0 .25rem;padding:0}.upcoming-events-widget .mptt-widget.events-list .event .column-title,.upcoming-events-widget .mptt-widget.events-list .event .event-user,.upcoming-events-widget .mptt-widget.events-list .event .timeslot{margin:.25rem 0 0;padding:0}.upcoming-events-widget .mptt-widget.events-list .event.mptt-colorized .event-title{margin:0 0 .25rem;padding:0;color:inherit}.upcoming-events-widget .mptt-widget.events-list .event.mptt-colorized .event-title .event-link{color:inherit}.mptt-hidden{display:none;opacity:0;clear:both}.mprm_ie_browser .mptt-inner-event-content{width:100%}.mptt-container{list-style:none}.mptt-shortcode-wrapper .mptt-shortcode-event .mptt-event-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.mptt-shortcode-wrapper .mptt-shortcode-event .mptt-event-container:hover{display:-webkit-box;display:-ms-flexbox;display:flex}.mptt-shortcode-wrapper .mptt-shortcode-event.mptt-event-vertical-top .mptt-event-container{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-ms-flex-line-pack:start;align-content:flex-start}.mptt-shortcode-wrapper .mptt-shortcode-event.mptt-event-vertical-middle .mptt-event-container{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center}.mptt-shortcode-wrapper .mptt-shortcode-event.mptt-event-vertical-bottom .mptt-event-container{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;-ms-flex-line-pack:end;align-content:flex-end}.mptt-shortcode-wrapper .mptt-shortcode-table{margin-top:1rem}.mptt-shortcode-wrapper .mptt-shortcode-table tr.mptt-shortcode-row th{background-color:#f0f0f0;border:1px solid #fff;text-align:center;color:#404040;padding:.25rem 1rem;overflow-wrap:normal;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.mptt-shortcode-wrapper .mptt-shortcode-table tbody tr:nth-child(2n+2){background-color:#f0f0f0}.mptt-shortcode-wrapper .mptt-shortcode-table tbody td{position:relative;border:1px solid #fff;padding:0;line-height:normal;-webkit-box-sizing:initial;box-sizing:initial}.mptt-shortcode-wrapper .mptt-shortcode-table tbody td.event{background-color:#3b5998}.mptt-shortcode-wrapper .mptt-shortcode-table tbody td.mptt-shortcode-hours{padding:0 1rem!important;white-space:nowrap;width:1%;vertical-align:middle}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container{position:absolute;top:0;right:0;left:0;z-index:3;overflow:hidden;outline:#fff solid 1px;color:inherit}.mptt-shortcode-wrapper .mptt-shortcode-table.mptt-theme-mode tbody td.event .event-user img,.mptt-theme-mode-event.event .event-user img{position:relative}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container:hover{background-color:inherit;overflow:visible;z-index:10}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container:hover .event-title{text-decoration:underline}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container p{color:inherit}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container .event-title{color:inherit;display:inline-block;margin:.5rem .25rem .25rem;text-decoration:none;font-size:1.1em}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container .event-subtitle,.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container .event-user{margin:.25rem;font-size:.8em}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container .event-user .avatar{border-radius:50%;margin-right:3px;display:inline-block;vertical-align:middle}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container .event-description{font-size:.8em;margin:.25rem;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.mptt-shortcode-wrapper .mptt-shortcode-table tbody .mptt-event-container .timeslot{font-size:.9em;margin:.25rem}.mptt-shortcode-wrapper .mptt-shortcode-table.mptt-theme-mode tbody tr:nth-child(2n+2) td.event{background-color:inherit}.mptt-shortcode-wrapper .mptt-shortcode-table.mptt-theme-mode tbody td.event{background-color:#fff}.mptt-shortcode-wrapper .mptt-navigation-tabs{margin:0 0 1rem;list-style-type:none}.mptt-shortcode-wrapper .mptt-navigation-tabs li{display:inline-block;padding:.25rem .5rem .25rem 0}.mptt-shortcode-wrapper .mptt-shortcode-list{display:none}.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column,.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-column-title{margin:2rem 0}.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-events-list{list-style:none;margin:2rem;padding:0}.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-events-list .mptt-list-event{list-style:none;margin-bottom:2rem;padding:0 0 0 2rem;border-left:3px solid #3b5998}.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-events-list .mptt-list-event .event-user,.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-events-list .mptt-list-event .mptt-event-title,.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-events-list .mptt-list-event .timeslot{margin:.25rem 0 0;padding:0}.mptt-shortcode-wrapper .mptt-shortcode-list .mptt-column .mptt-events-list .mptt-list-event .event-description{margin:.25rem 0 0;padding:0;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}.mptt-shortcode-wrapper.mptt-table-fixed{overflow-x:scroll}.mptt-shortcode-wrapper.mptt-table-fixed .mptt-shortcode-table{table-layout:auto}.mptt-shortcode-wrapper.mptt-table-fixed tbody,.mptt-shortcode-wrapper.mptt-table-fixed thead{width:100%}.mptt-shortcode-wrapper td{vertical-align:middle}@media (max-width:767px){.mptt-main-wrapper .mptt-content,.mptt-main-wrapper .mptt-sidebar{padding:0 2rem 2rem;width:100%}.mptt-shortcode-wrapper .mptt-shortcode-list{display:block}.mptt-shortcode-wrapper.mptt-table-fixed .mptt-shortcode-list{display:none!important}.mptt-shortcode-wrapper.mptt-table-fixed .mptt-shortcode-table{display:table}.mptt-shortcode-wrapper.mptt-table-fixed .mptt-shortcode-table td.event{min-width:200px}.mptt-shortcode-wrapper.mptt-table-responsive .mptt-shortcode-table{display:none!important}}.mptt-clearfix:after,.mptt-clearfix:before{display:table;content:"";line-height:0}.mptt-clearfix:after{clear:both}.twentysixteen.mptt-shortcode-wrapper .event-title{-webkit-box-shadow:none;box-shadow:none}.twentyfifteen.mptt-shortcode-wrapper .event-title{border-bottom:none}.twentyfourteen .mptt-content{width:100%;margin:0}
media/img/shortcode-icon.png ADDED
Binary file
media/img/shortcode-icon.psd ADDED
Binary file
media/js/blocks/dist/index.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function n(l){if(t[l])return t[l].exports;var a=t[l]={i:l,l:!1,exports:{}};return e[l].call(a.exports,a,a.exports,n),a.l=!0,a.exports}n.m=e,n.c=t,n.d=function(e,t,l){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:l})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var l=Object.create(null);if(n.r(l),Object.defineProperty(l,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(l,a,function(t){return e[t]}.bind(null,a));return l},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=3)}([function(e,t){e.exports=lodash},,,function(e,t,n){"use strict";n.r(t);var l=n(0);function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){for(var n=0;n<t.length;n++){var l=t[n];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,l.key,l)}}function r(e){return(r=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function i(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function u(e,t){return(u=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}var c=wp.i18n.__,p=wp.element.Component,m=wp.editor.InspectorControls,s=wp.components,b=s.SelectControl,f=s.CheckboxControl,v=s.PanelBody,h=s.TextControl,d=function(e){function t(){var e,n,l;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),n=this,(e=!(l=r(t).apply(this,arguments))||"object"!==a(l)&&"function"!=typeof l?i(n):l).setOptions=e.setOptions.bind(i(e)),e}var n,s,d;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&u(e,t)}(t,p),n=t,(s=[{key:"setOptions",value:function(e){var t=[];return e&&(t=e.map(function(e){return{value:e.id.toString(),label:Object(l.get)(e,["title","raw"])||Object(l.get)(e,["name"])}})),t}},{key:"render",value:function(){var e=this.props,t=e.attributes,n=t.col,l=t.events,a=t.event_categ,o=t.increment,r=t.view,i=t.label,u=t.hide_label,p=t.hide_hrs,s=t.hide_empty_rows,d=t.title,y=t.time,g=t.sub_title,_=t.description,w=t.user,E=t.group,C=t.disable_event_url,O=t.text_align,R=t.text_align_vertical,S=t.id,j=t.custom_class,k=t.row_height,T=t.font_size,x=t.responsive,P=e.selectedEvents,D=e.selectedColumns,I=e.selectedEventCategories,z=e.setAttributes;return React.createElement(m,null,React.createElement(v,{title:c("Settings","mp-timetable")},React.createElement(b,{multiple:!0,size:"7",label:c("Columns","mp-timetable"),help:c("In order to display multiple points hold ctrl/cmd button.","mp-timetable"),value:n,onChange:function(e){return z({col:e})},options:this.setOptions(D)}),React.createElement(b,{multiple:!0,size:"7",label:c("Specific events","mp-timetable"),value:l,onChange:function(e){return z({events:e})},options:this.setOptions(P)}),React.createElement(b,{multiple:!0,size:"7",label:c("Event categories","mp-timetable"),value:a,onChange:function(e){return z({event_categ:e})},options:this.setOptions(I)}),React.createElement(f,{label:c("Title","mp-timetable"),checked:"1"==d,onChange:function(e){z({title:e?"1":"0"})}}),React.createElement(f,{label:c("Time","mp-timetable"),checked:"1"==y,onChange:function(e){z({time:e?"1":"0"})}}),React.createElement(f,{label:c("Subtitle","mp-timetable"),checked:"1"==g,onChange:function(e){z({sub_title:e?"1":"0"})}}),React.createElement(f,{label:c("Description","mp-timetable"),checked:"1"==_,onChange:function(e){z({description:e?"1":"0"})}}),React.createElement(f,{label:c("Event Head","mp-timetable"),checked:"1"==w,onChange:function(e){z({user:e?"1":"0"})}}),React.createElement(h,{label:c("Block height in pixels","mp-timetable"),type:"number",value:isNaN(k)?0:parseInt(k),onChange:function(e){z({row_height:e.toString()})},min:1,step:1}),React.createElement(h,{label:c("Base font size","mp-timetable"),help:c("Base font size for the table. Example 12px, 2em, 80%.","mp-timetable"),value:T,onChange:function(e){return z({font_size:e})}}),React.createElement(b,{label:c("Time frame for event","mp-timetable"),value:o,onChange:function(e){return z({increment:e})},options:[{value:"1",label:c("Hour (1h)","mp-timetable")},{value:"0.5",label:c("Half hour (30min)","mp-timetable")},{value:"0.25",label:c("Quarter hour (15min)","mp-timetable")}]}),React.createElement(b,{label:c("Filter events style","mp-timetable"),value:r,onChange:function(e){return z({view:e})},options:[{value:"dropdown_list",label:c("Dropdown list","mp-timetable")},{value:"tabs",label:c("Tabs","mp-timetable")}]}),React.createElement(h,{label:c("Filter title to display all events","mp-timetable"),value:i,onChange:function(e){return z({label:e})}}),React.createElement(b,{label:c("Hide 'All Events' option","mp-timetable"),value:u,onChange:function(e){return z({hide_label:e})},options:[{value:"0",label:c("No","mp-timetable")},{value:"1",label:c("Yes","mp-timetable")}]}),React.createElement(b,{label:c("Hide column with hours","mp-timetable"),value:p,onChange:function(e){return z({hide_hrs:e})},options:[{value:"0",label:c("No","mp-timetable")},{value:"1",label:c("Yes","mp-timetable")}]}),React.createElement(b,{label:c("Do not display empty rows","mp-timetable"),value:s,onChange:function(e){return z({hide_empty_rows:e})},options:[{value:"0",label:c("No","mp-timetable")},{value:"1",label:c("Yes","mp-timetable")}]}),React.createElement(b,{label:c("Merge cells with common events","mp-timetable"),value:E,onChange:function(e){return z({group:e})},options:[{value:"0",label:c("No","mp-timetable")},{value:"1",label:c("Yes","mp-timetable")}]}),React.createElement(b,{label:c("Disable event link","mp-timetable"),value:C,onChange:function(e){return z({disable_event_url:e})},options:[{value:"0",label:c("No","mp-timetable")},{value:"1",label:c("Yes","mp-timetable")}]}),React.createElement(b,{label:c("Horizontal align","mp-timetable"),value:O,onChange:function(e){return z({text_align:e})},options:[{value:"center",label:c("center","mp-timetable")},{value:"left",label:c("left","mp-timetable")},{value:"right",label:c("right","mp-timetable")}]}),React.createElement(b,{label:c("Vertical align","mp-timetable"),value:R,onChange:function(e){return z({text_align_vertical:e})},options:[{value:"default",label:c("Default","mp-timetable")},{value:"top",label:c("top","mp-timetable")},{value:"middle",label:c("middle","mp-timetable")},{value:"bottom",label:c("bottom","mp-timetable")}]}),React.createElement(h,{label:c("Unique ID","mp-timetable"),help:c("If you use more than one table on a page specify the unique ID for a timetable. It is usually all lowercase and contains only letters, numbers, and hyphens.","mp-timetable"),value:S,onChange:function(e){return z({id:e})}}),React.createElement(h,{label:c("CSS class","mp-timetable"),value:j,onChange:function(e){return z({custom_class:e})}}),React.createElement(b,{label:c("Mobile behavior","mp-timetable"),value:x,onChange:function(e){return z({responsive:e})},options:[{value:"0",label:c("Table","mp-timetable")},{value:"1",label:c("List","mp-timetable")}]})))}}])&&o(n.prototype,s),d&&o(n,d),t}();function y(e){return(y="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function g(e,t){for(var n=0;n<t.length;n++){var l=t[n];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,l.key,l)}}function _(e,t){return!t||"object"!==y(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function w(e){return(w=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function E(e,t){return(E=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}wp.i18n.__;var C=wp.element,O=C.Component,R=C.Fragment,S=wp.compose.compose,j=wp.components,k=j.Disabled,T=j.ServerSideRender,x=wp.data.withSelect,P=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),_(this,w(t).apply(this,arguments))}var n,a,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&E(e,t)}(t,O),n=t,(a=[{key:"initTable",value:function(){var e=setInterval(function(){$(".mptt-shortcode-wrapper").length&&!$(".mptt-shortcode-wrapper").hasClass("table-init")&&(clearInterval(e),window.mptt.tableInit())},1)}},{key:"componentDidUpdate",value:function(e,t){Object(l.isEqual)(this.props.attributes,e.attributes)||this.initTable()}},{key:"componentDidMount",value:function(){this.initTable()}},{key:"render",value:function(){var e=this.props.attributes;e.events,e.event_categ;return React.createElement(R,null,React.createElement(d,this.props),React.createElement(k,null,React.createElement(T,{block:"mp-timetable/timetable",attributes:this.props.attributes})))}}])&&g(n.prototype,a),o&&g(n,o),t}(),D=S([x(function(e,t){var n=e("core"),a=n.getEntityRecords,o=(n.getCategories,a("postType","mp-event",{per_page:-1,orderby:"title",order:"asc"})),r=a("postType","mp-column",{per_page:-1}),i=a("taxonomy","mp-event_category",{per_page:-1});return{selectedEvents:o?o.map(function(e){return Object(l.pick)(e,["id","title"])}):null,selectedColumns:r?r.map(function(e){return Object(l.pick)(e,["id","title"])}):null,selectedEventCategories:i?i.map(function(e){return Object(l.pick)(e,["id","name"])}):null}})])(P);(0,wp.blocks.registerBlockType)("mp-timetable/timetable",{title:(0,wp.i18n.__)("Timetable","mp-timetable"),category:"common",icon:"calendar",supports:{align:["wide","full"]},getEditWrapperProps:function(e){var t=e.align;if(["wide","full"].includes(t))return{"data-align":t}},edit:D,save:function(){return null}})}]);
media/js/blocks/src/index.js ADDED
@@ -0,0 +1 @@
 
1
+ import './timetable';
media/js/blocks/src/timetable/edit.js ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Inspector from './inspector';
2
+
3
+ import { pick, isEqual } from "lodash";
4
+
5
+ const {__} = wp.i18n;
6
+ const { Component, Fragment } = wp.element;
7
+ const { compose } = wp.compose;
8
+
9
+ const {
10
+ Disabled,
11
+ ServerSideRender
12
+ } = wp.components;
13
+
14
+ const {
15
+ withSelect
16
+ } = wp.data;
17
+
18
+ class Edit extends Component {
19
+ constructor() {
20
+ super(...arguments);
21
+ }
22
+
23
+ initTable(){
24
+ //Set timer and check when table is load fully, and then initialize table data, and after stop timer
25
+ const waitLoadTable = setInterval( () => {
26
+ if ($('.mptt-shortcode-wrapper').length && !$('.mptt-shortcode-wrapper').hasClass('table-init')){
27
+ clearInterval(waitLoadTable);
28
+ window.mptt.tableInit();
29
+ }
30
+ }, 1);
31
+ }
32
+
33
+ componentDidUpdate(prevProps, prevState) {
34
+ if (!isEqual(this.props.attributes, prevProps.attributes)) {
35
+ this.initTable();
36
+ }
37
+ }
38
+
39
+ componentDidMount() {
40
+ this.initTable();
41
+ }
42
+
43
+ render() {
44
+
45
+ const {
46
+ attributes: {
47
+ events,
48
+ event_categ
49
+ }
50
+ } = this.props;
51
+
52
+ return (
53
+ <Fragment>
54
+ <Inspector {...this.props }/>
55
+ <Disabled>
56
+ <ServerSideRender
57
+ block="mp-timetable/timetable"
58
+ attributes={this.props.attributes}
59
+ />
60
+ </Disabled>
61
+ </Fragment>
62
+ );
63
+ }
64
+ }
65
+
66
+ export default compose([
67
+ withSelect(( select, props ) => {
68
+ const { getEntityRecords, getCategories } = select( "core" );
69
+
70
+ let events = getEntityRecords( "postType", "mp-event", {per_page: -1, orderby: 'title', order: 'asc'} );
71
+ let columns = getEntityRecords( "postType", "mp-column", {per_page: -1} );
72
+ let eventCategories = getEntityRecords( "taxonomy", "mp-event_category", {per_page: -1} );
73
+
74
+ return {
75
+ selectedEvents: events ? events .map((event) => {
76
+ return pick( event, [ 'id', 'title' ])
77
+ }) : null,
78
+
79
+ selectedColumns: columns ? columns.map((column) => {
80
+ return pick( column, [ 'id', 'title' ])
81
+ }) : null,
82
+
83
+ selectedEventCategories: eventCategories ? eventCategories.map((categorie) => {
84
+ return pick( categorie, [ 'id', 'name' ])
85
+ }) : null
86
+ };
87
+ }),
88
+ ])(Edit);
media/js/blocks/src/timetable/index.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import edit from './edit';
2
+
3
+ const {
4
+ registerBlockType,
5
+ } = wp.blocks;
6
+
7
+ const { __ } = wp.i18n;
8
+
9
+ export default registerBlockType(
10
+ 'mp-timetable/timetable',
11
+ {
12
+ title: __('Timetable', 'mp-timetable'),
13
+ category: 'common',
14
+ icon: 'calendar',
15
+ supports: {
16
+ align: [ 'wide', 'full' ],
17
+ },
18
+ getEditWrapperProps( attributes ) {
19
+ const { align } = attributes;
20
+ if ( [ 'wide', 'full' ].includes( align ) ) {
21
+ return { 'data-align': align };
22
+ }
23
+ },
24
+ edit,
25
+ save: () => {
26
+ return null;
27
+ },
28
+ }
29
+ )
media/js/blocks/src/timetable/inspector.js ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { get } from "lodash";
2
+
3
+ const {__} = wp.i18n;
4
+ const {Component} = wp.element;
5
+
6
+ const {
7
+ InspectorControls,
8
+ } = wp.editor;
9
+
10
+ const {
11
+ SelectControl,
12
+ CheckboxControl,
13
+ PanelBody,
14
+ TextControl,
15
+
16
+ } = wp.components;
17
+
18
+ class Inspector extends Component {
19
+ constructor() {
20
+ super(...arguments);
21
+
22
+ this.setOptions = this.setOptions.bind( this );
23
+ }
24
+
25
+ setOptions(data) {
26
+ let options = [];
27
+ if (data) {
28
+ options = data.map((event => {
29
+ return {
30
+ value: event.id.toString(),
31
+ label: get( event, [ 'title', 'raw' ] ) || get( event, [ 'name' ] )
32
+ }
33
+ }));
34
+ }
35
+ return options;
36
+ }
37
+
38
+ render() {
39
+
40
+ const {
41
+ attributes: {
42
+ col,
43
+ events,
44
+ event_categ,
45
+
46
+ increment,
47
+ view,
48
+ label,
49
+
50
+ hide_label,
51
+ hide_hrs,
52
+ hide_empty_rows,
53
+
54
+ title,
55
+ time,
56
+ sub_title,
57
+ description,
58
+ user,
59
+
60
+ group,
61
+ disable_event_url,
62
+ text_align,
63
+ text_align_vertical,
64
+ id,
65
+ custom_class,
66
+
67
+ row_height,
68
+ font_size,
69
+ responsive,
70
+ },
71
+
72
+ selectedEvents,
73
+ selectedColumns,
74
+ selectedEventCategories,
75
+
76
+ setAttributes,
77
+ } = this.props;
78
+
79
+ return (
80
+ <InspectorControls>
81
+ <PanelBody
82
+ title={__('Settings', 'mp-timetable')}
83
+ >
84
+ <SelectControl
85
+ multiple
86
+ size="7"
87
+ label={__('Columns', 'mp-timetable')}
88
+ help={__('In order to display multiple points hold ctrl/cmd button.', 'mp-timetable')}
89
+ value={col}
90
+ onChange={col => setAttributes({col})}
91
+ options={this.setOptions(selectedColumns)}
92
+ />
93
+ <SelectControl
94
+ multiple
95
+ size="7"
96
+ label={__('Specific events', 'mp-timetable')}
97
+ value={events}
98
+ onChange={events => setAttributes({events})}
99
+ options={this.setOptions(selectedEvents )}
100
+ />
101
+ <SelectControl
102
+ multiple
103
+ size="7"
104
+ label={__('Event categories', 'mp-timetable')}
105
+ value={event_categ}
106
+ onChange={event_categ => setAttributes({event_categ})}
107
+ options={this.setOptions(selectedEventCategories)}
108
+ />
109
+ <CheckboxControl
110
+ label={__('Title', 'mp-timetable')}
111
+ checked={title == '1' ? true : false}
112
+ onChange={(title) => {
113
+ setAttributes({title: title ? '1' : '0'})
114
+ }}
115
+ />
116
+ <CheckboxControl
117
+ label={__('Time', 'mp-timetable')}
118
+ checked={time == '1' ? true : false}
119
+ onChange={(time) => { setAttributes({time: time ? '1' : '0'}) }}
120
+ />
121
+ <CheckboxControl
122
+ label={__('Subtitle', 'mp-timetable')}
123
+ checked={sub_title == '1' ? true : false}
124
+ onChange={(sub_title) => { setAttributes({sub_title: sub_title ? '1' : '0'}) }}
125
+ />
126
+ <CheckboxControl
127
+ label={__('Description', 'mp-timetable')}
128
+ checked={description == '1' ? true : false}
129
+ onChange={(description) => { setAttributes({description: description ? '1' : '0'}) }}
130
+ />
131
+ <CheckboxControl
132
+ label={__('Event Head', 'mp-timetable')}
133
+ checked={user == '1' ? true : false}
134
+ onChange={(user) => { setAttributes({user: user ? '1' : '0'}) }}
135
+ />
136
+ <TextControl
137
+ label={__('Block height in pixels', 'mp-timetable')}
138
+ type={'number'}
139
+ value={isNaN(row_height) ? 0 : parseInt(row_height)}
140
+ onChange={row_height => {
141
+ setAttributes({ row_height: row_height.toString() });
142
+ }}
143
+ min={1}
144
+ step={1}
145
+ />
146
+ <TextControl
147
+ label={__('Base font size', 'mp-timetable')}
148
+ help={__('Base font size for the table. Example 12px, 2em, 80%.', 'mp-timetable')}
149
+ value={font_size}
150
+ onChange={font_size => setAttributes({font_size})}
151
+ />
152
+ <SelectControl
153
+ label={__('Time frame for event', 'mp-timetable')}
154
+ value={increment}
155
+ onChange={increment => setAttributes({ increment })}
156
+ options={[
157
+ { value: '1' , label: __( 'Hour (1h)' , 'mp-timetable' ) },
158
+ { value: '0.5' , label: __( 'Half hour (30min)' , 'mp-timetable' ) },
159
+ { value: '0.25', label: __( 'Quarter hour (15min)' , 'mp-timetable' ) },
160
+ ]}
161
+ />
162
+ <SelectControl
163
+ label={__('Filter events style', 'mp-timetable')}
164
+ value={view}
165
+ onChange={view => setAttributes({ view })}
166
+ options={[
167
+ { value: 'dropdown_list', label: __( 'Dropdown list', 'mp-timetable' ) },
168
+ { value: 'tabs' , label: __( 'Tabs' , 'mp-timetable' ) },
169
+ ]}
170
+ />
171
+ <TextControl
172
+ label={__('Filter title to display all events', 'mp-timetable')}
173
+ value={label}
174
+ onChange={label => setAttributes({ label })}
175
+ />
176
+ <SelectControl
177
+ label={__('Hide \'All Events\' option', 'mp-timetable')}
178
+ value={hide_label}
179
+ onChange={hide_label => setAttributes({ hide_label })}
180
+ options={[
181
+ { value: '0', label: __( 'No' , 'mp-timetable' ) },
182
+ { value: '1', label: __( 'Yes', 'mp-timetable' ) },
183
+ ]}
184
+ />
185
+ <SelectControl
186
+ label={__('Hide column with hours', 'mp-timetable')}
187
+ value={hide_hrs}
188
+ onChange={hide_hrs => setAttributes({ hide_hrs })}
189
+ options={[
190
+ { value: '0', label: __( 'No' , 'mp-timetable' ) },
191
+ { value: '1', label: __( 'Yes', 'mp-timetable' ) },
192
+ ]}
193
+ />
194
+ <SelectControl
195
+ label={__('Do not display empty rows', 'mp-timetable')}
196
+ value={hide_empty_rows}
197
+ onChange={hide_empty_rows => setAttributes({ hide_empty_rows })}
198
+ options={[
199
+ { value: '0', label: __( 'No' , 'mp-timetable' ) },
200
+ { value: '1', label: __( 'Yes', 'mp-timetable' ) },
201
+ ]}
202
+ />
203
+ <SelectControl
204
+ label={__('Merge cells with common events', 'mp-timetable')}
205
+ value={group}
206
+ onChange={group => setAttributes({ group })}
207
+ options={[
208
+ { value: '0', label: __( 'No' , 'mp-timetable' ) },
209
+ { value: '1', label: __( 'Yes', 'mp-timetable' ) },
210
+ ]}
211
+ />
212
+ <SelectControl
213
+ label={__('Disable event link', 'mp-timetable')}
214
+ value={disable_event_url}
215
+ onChange={disable_event_url => setAttributes({ disable_event_url })}
216
+ options={[
217
+ { value: '0', label: __( 'No' , 'mp-timetable' ) },
218
+ { value: '1', label: __( 'Yes', 'mp-timetable' ) },
219
+ ]}
220
+ />
221
+ <SelectControl
222
+ label={__('Horizontal align', 'mp-timetable')}
223
+ value={text_align}
224
+ onChange={text_align => setAttributes({ text_align })}
225
+ options={[
226
+ { value: 'center', label: __( 'center', 'mp-timetable' ) },
227
+ { value: 'left', label: __( 'left' , 'mp-timetable' ) },
228
+ { value: 'right', label: __( 'right' , 'mp-timetable' ) },
229
+ ]}
230
+ />
231
+ <SelectControl
232
+ label={__('Vertical align', 'mp-timetable')}
233
+ value={text_align_vertical}
234
+ onChange={text_align_vertical => setAttributes({ text_align_vertical })}
235
+ options={[
236
+ { value: 'default', label: __( 'Default', 'mp-timetable' ) },
237
+ { value: 'top', label: __( 'top' , 'mp-timetable' ) },
238
+ { value: 'middle', label: __( 'middle' , 'mp-timetable' ) },
239
+ { value: 'bottom', label: __( 'bottom' , 'mp-timetable' ) },
240
+ ]}
241
+ />
242
+ <TextControl
243
+ label={__('Unique ID', 'mp-timetable')}
244
+ help={__('If you use more than one table on a page specify the unique ID for a timetable. It is usually all lowercase and contains only letters, numbers, and hyphens.', 'mp-timetable')}
245
+ value={id}
246
+ onChange={id => setAttributes({id})}
247
+ />
248
+ <TextControl
249
+ label={__('CSS class', 'mp-timetable')}
250
+ value={custom_class}
251
+ onChange={custom_class => setAttributes({custom_class})}
252
+ />
253
+ <SelectControl
254
+ label={__('Mobile behavior', 'mp-timetable')}
255
+ value={responsive}
256
+ onChange={responsive => setAttributes({responsive})}
257
+ options={[
258
+ { value: '0', label: __( 'Table' , 'mp-timetable' ) },
259
+ { value: '1', label: __( 'List', 'mp-timetable' ) },
260
+ ]}
261
+ />
262
+
263
+ </PanelBody>
264
+ </InspectorControls>
265
+ );
266
+ }
267
+ }
268
+
269
+ export default (Inspector);
media/js/events/event.js ADDED
@@ -0,0 +1,937 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*global jQuery:false, console:false, _:false, CommonManager:false,Registry:false, wp:false, MPTT:false*/
2
+
3
+ Registry.register("Event",
4
+ (function($) {
5
+ "use strict";
6
+
7
+ var state;
8
+
9
+ function createInstance() {
10
+
11
+ return {
12
+ event_id: '',
13
+ eventsData: {},
14
+ /**
15
+ * Init
16
+ */
17
+ init: function() {
18
+ state.initTimePicker();
19
+ state.addEventButton();
20
+ state.initDeleteButtons();
21
+ state.initEditButtons();
22
+ state.initColorPicker();
23
+ state.initDatePicker();
24
+ state.columnRadioBox();
25
+ },
26
+ /**
27
+ * Init time picker
28
+ */
29
+ initTimePicker: function() {
30
+ var timeFormat = Boolean(parseInt($('#time_format').val()));
31
+ $('#event_start').timepicker({
32
+ showPeriod: timeFormat, // Define whether or not to show AM/PM with selected time. (default: false)
33
+ showPeriodLabels: timeFormat,
34
+ defaultTime: '00:00'
35
+ });
36
+
37
+ $('#event_end').timepicker({
38
+ showPeriod: timeFormat, // Define whether or not to show AM/PM with selected time. (default: false)
39
+ showPeriodLabels: timeFormat,
40
+ defaultTime: '00:00'
41
+ });
42
+ },
43
+ /**
44
+ * Init widget slider
45
+ * @param selector
46
+ * @param autoScroll
47
+ */
48
+ initSlider: function(selector, autoScroll) {
49
+ var play = _.isUndefined(autoScroll) ? false : Boolean(autoScroll);
50
+ var id = selector.replace(/^\D+/g, '');
51
+ $(selector).carouFredSel({
52
+ items: {
53
+ visible: 3
54
+ },
55
+ direction: "up",
56
+ scroll: {
57
+ items: 1,
58
+ easing: "swing",
59
+ pauseOnHover: true,
60
+ onAfter: function(data) {
61
+ data.items.old.each(function() {
62
+ $(this).removeClass('visible');
63
+ }
64
+ );
65
+ data.items.visible.each(function() {
66
+ $(this).addClass('visible');
67
+ }
68
+ );
69
+ }
70
+ },
71
+ auto: {
72
+ play: play,
73
+ timeoutDuration: 3000
74
+ },
75
+ prev: {
76
+ button: "#mp_prev_button" + id
77
+ },
78
+ next: {
79
+ button: "#mp_next_button" + id
80
+ }
81
+ });
82
+
83
+ $(selector).trigger("currentVisible", function(items) {
84
+ items.addClass("visible");
85
+ });
86
+ state.setColorSettings(selector + ' ' + '.mptt-colorized');
87
+ },
88
+ /**
89
+ * init Delete Button
90
+ */
91
+ initDeleteButton: function() {
92
+ var $events = $('#events-list');
93
+
94
+ $events.find('li.event').find('i.operation-button.dashicons-no.dashicons').off('click').on('click', function() {
95
+ if ($events.find('li.event').length > 1) {
96
+ $(this).parents('li.event').remove();
97
+ } else {
98
+ $events.remove();
99
+ }
100
+ });
101
+ },
102
+ /**
103
+ * Init color picker
104
+ */
105
+ initColorPicker: function(parent) {
106
+ if (_.isUndefined(parent)) {
107
+ parent = '';
108
+ }
109
+ var selectorColorInput = $(parent + ' input.clr-picker');
110
+ var selectorTextInput = $(parent + ' input.regular-text');
111
+ selectorColorInput.spectrum("destroy");
112
+
113
+ // init color picker
114
+ selectorColorInput.spectrum({
115
+ preferredFormat: "rgb",
116
+ showInput: true,
117
+ showAlpha: true,
118
+ allowEmpty: true,
119
+ palette: [
120
+ ["#000", "#444", "#666", "#999", "#ccc", "#eee", "#f3f3f3", "#fff"],
121
+ ["#f00", "#f90", "#ff0", "#0f0", "#0ff", "#00f", "#90f", "#f0f"],
122
+ ["#f4cccc", "#fce5cd", "#fff2cc", "#d9ead3", "#d0e0e3", "#cfe2f3", "#d9d2e9", "#ead1dc"],
123
+ ["#ea9999", "#f9cb9c", "#ffe599", "#b6d7a8", "#a2c4c9", "#9fc5e8", "#b4a7d6", "#d5a6bd"],
124
+ ["#e06666", "#f6b26b", "#ffd966", "#93c47d", "#76a5af", "#6fa8dc", "#8e7cc3", "#c27ba0"],
125
+ ["#c00", "#e69138", "#f1c232", "#6aa84f", "#45818e", "#3d85c6", "#674ea7", "#a64d79"],
126
+ ["#900", "#b45f06", "#bf9000", "#38761d", "#134f5c", "#0b5394", "#351c75", "#741b47"],
127
+ ["#600", "#783f04", "#7f6000", "#274e13", "#0c343d", "#073763", "#20124d", "#4c1130"]
128
+ ],
129
+ showPalette: true,
130
+ show: function(color) {
131
+ $(this).val(color);
132
+ },
133
+ hide: function(color) {
134
+ var parent = $(this).parents('.select-color');
135
+ parent.find('.regular-text').val($(this).val());
136
+ },
137
+ change: function(color) {
138
+ var parent = $(this).parents('.select-color');
139
+ parent.find('input:not([type="hidden"])').val($(this).val());
140
+ }
141
+ });
142
+
143
+ //change color preview block
144
+ selectorTextInput.off('keyup').on('keyup', function() {
145
+ var parentTr = $(this).parents('.select-color');
146
+ var spectrumElement = parentTr.find('.clr-picker');
147
+ var colorElement = parentTr.find(".regular-text").val();
148
+ var preview_inner = parentTr.find(".sp-preview-inner");
149
+ preview_inner.css({
150
+ 'background-color': colorElement
151
+ });
152
+ spectrumElement.spectrum("set", colorElement);
153
+ });
154
+ },
155
+ /**
156
+ * Add event
157
+ */
158
+ addEventButton: function() {
159
+ $(document).on('click.admin', '#add_mp_event', function() {
160
+ if ($(this).hasClass('edit')) {
161
+ state.updateEventData();
162
+ } else {
163
+ state.renderEventItem();
164
+ }
165
+ });
166
+ },
167
+ /**
168
+ * init event data delete button
169
+ */
170
+ initDeleteButtons: function() {
171
+ $(document).on('click.admin', '#events-list .delete-event-button', function() {
172
+ var id = $(this).attr('data-id');
173
+ state.deleteEvent(id);
174
+ });
175
+ },
176
+ /**
177
+ * init event data edit button
178
+ */
179
+ initEditButtons: function() {
180
+ $(document).on('click.admin', '#events-list .edit-event-button', function() {
181
+ var id = $(this).attr('data-id'),
182
+ $tr = $(this).parent().parent();
183
+ $(this).parent().find('.spinner').addClass('is-active');
184
+
185
+ Registry._get("adminFunctions").wpAjax({
186
+ controller: "events",
187
+ action: "get_event_data",
188
+ id: id
189
+ },
190
+ function(data) {
191
+ var $addMpEvent = $('#add_mp_event');
192
+ var $events = $('#events-list');
193
+ $events.find('.spinner').removeClass('is-active');
194
+ $events.find(' tr').removeClass('active');
195
+ $tr.addClass('active');
196
+
197
+ $('#event_start').val(data.event_start);
198
+ $('#event_end').val(data.event_end);
199
+ $('#description').val(data.description);
200
+ $('#user_id').val(data.user_id);
201
+ $('#weekday_id').val(data.column_id);
202
+
203
+ $addMpEvent.addClass('edit');
204
+ $addMpEvent.val('Update');
205
+
206
+ state.event_id = data.id;
207
+ },
208
+ function(data) {
209
+ console.warn(data);
210
+ }
211
+ );
212
+ });
213
+ },
214
+ /**
215
+ * Delete event data by id
216
+ *
217
+ * @param id
218
+ */
219
+ deleteEvent: function(id) {
220
+ Registry._get("adminFunctions").wpAjax(
221
+ {
222
+ controller: "events",
223
+ action: "delete",
224
+ id: id
225
+ },
226
+ function(data) {
227
+ var $deleteEvent = $('#events-list').find('tr[data-id="' + id + '"]');
228
+ if ($deleteEvent.length) {
229
+ $deleteEvent.remove();
230
+ }
231
+ },
232
+ function(data) {
233
+ console.log(data);
234
+ }
235
+ );
236
+ },
237
+ /**
238
+ * Update event item
239
+ */
240
+ updateEventItem: function() {
241
+ var item = $('#events-list').find('tr[data-id="' + state.event_id + '"]');
242
+ var $userId = $('#user_id');
243
+
244
+ item.find('td.event-column').text($('#weekday_id').find('option:selected').text());
245
+ item.find('td.event-start').text($('#event_start').val());
246
+ item.find('td.event-end').text($('#event_end').val());
247
+
248
+ item.find('td.event-user-id').text(( $userId.val() === '-1') ? '' : $userId.find('option:selected').text());
249
+ item.find('td.event-description').text($('#description').val());
250
+
251
+ state.event_id = null;
252
+ $('#add_mp_event').removeClass('edit').val('Add New');
253
+ },
254
+ /**
255
+ * Update Event data
256
+ */
257
+ updateEventData: function() {
258
+ var $addEventTable = $('#add_event_table').find('.spinner');
259
+
260
+ $addEventTable.addClass('is-active');
261
+
262
+ Registry._get("adminFunctions").wpAjax({
263
+ controller: "events",
264
+ action: "update_event_data",
265
+ data: {
266
+ id: Registry._get("Event").event_id,
267
+ event_start: $('#event_start').val(),
268
+ event_end: $('#event_end').val(),
269
+ description: $('#description').val(),
270
+ user_id: $('#user_id').val(),
271
+ weekday_ids: $('#weekday_id').val()
272
+ }
273
+ },
274
+ function() {
275
+ $addEventTable.removeClass('is-active');
276
+ state.updateEventItem();
277
+ state.clearTable();
278
+ },
279
+ function(data) {
280
+ $addEventTable.removeClass('is-active');
281
+ console.log(data);
282
+ }
283
+ );
284
+ },
285
+ /**
286
+ * Render event item
287
+ */
288
+ renderEventItem: function() {
289
+ var $weekdayId = $('#weekday_id');
290
+ var $userId = $('#user_id');
291
+ var column_ID = $weekdayId.find('option:selected').val();
292
+ var $eventStart = $('#event_start');
293
+ var $eventEnd = $('#event_end');
294
+ var $description = $('#description');
295
+
296
+ var template = {
297
+ tag: 'tr',
298
+ attrs: {},
299
+ content: [
300
+ {
301
+ tag: 'td',
302
+ attrs: {'style': 'display:none;'},
303
+ content: [
304
+ {
305
+ tag: 'input',
306
+ attrs: {
307
+ 'type': 'hidden',
308
+ 'name': 'event_data[' + column_ID + '][weekday_ids][]',
309
+ 'value': column_ID
310
+ }
311
+ },
312
+ {
313
+ tag: 'input',
314
+ attrs: {
315
+ 'type': 'hidden',
316
+ 'name': 'event_data[' + column_ID + '][event_start][]',
317
+ 'value': $eventStart.val()
318
+ }
319
+ },
320
+ {
321
+ tag: 'input',
322
+ attrs: {
323
+ 'type': 'hidden',
324
+ 'name': 'event_data[' + column_ID + '][event_end][]',
325
+ 'value': $eventEnd.val()
326
+ }
327
+ },
328
+ {
329
+ tag: 'input',
330
+ attrs: {
331
+ 'type': 'hidden',
332
+ 'name': 'event_data[' + column_ID + '][description][]',
333
+ 'value': $description.val()
334
+ }
335
+ },
336
+ {
337
+ tag: 'input',
338
+ attrs: {
339
+ 'type': 'hidden',
340
+ 'name': 'event_data[' + column_ID + '][user_id][]',
341
+ 'value': $userId.val()
342
+ }
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ tag: 'td',
348
+ attrs: {
349
+ 'class': 'event-column'
350
+ },
351
+ content: [$weekdayId.find('option:selected').text()]
352
+ },
353
+ {
354
+ tag: 'td',
355
+ attrs: {
356
+ 'class': 'event-start'
357
+ },
358
+ content: [$eventStart.val()]
359
+ },
360
+ {
361
+ tag: 'td',
362
+ attrs: {
363
+ 'class': 'event-end'
364
+ },
365
+ content: [$eventEnd.val()]
366
+ },
367
+ {
368
+ tag: 'td',
369
+ attrs: {
370
+ 'class': 'event-description'
371
+ },
372
+ content: [$description.val()]
373
+ },
374
+ {
375
+ tag: 'td',
376
+ attrs: {
377
+ 'class': 'event-user-id'
378
+ },
379
+ content: [( $userId.val() === '-1') ? '' : $userId.find('option:selected').text()]
380
+ },
381
+ {
382
+ tag: 'td',
383
+ attrs: {},
384
+ content: []
385
+ }
386
+ ]
387
+ };
388
+
389
+ var htmlObject = Registry._get("adminFunctions").getHtml(template);
390
+ $('#events-list').find('tbody').append(htmlObject);
391
+ $('.events-list-wrapper').scrollTop(1E10);
392
+ state.clearTable();
393
+ },
394
+ /**
395
+ * Set event height
396
+ *
397
+ * @param element
398
+ */
399
+ setEventHeight: function(element) {
400
+ var parent_height = element.parent().outerHeight(),
401
+ body = $('body');
402
+
403
+ var elementHeight = element.height();
404
+ var min_height = element.data('min-height');
405
+ var inner_height = element.find('.mptt-inner-event-content').height();
406
+
407
+ element.css('position', '').css('width', '').css('min-height', '');
408
+
409
+ if (!body.hasClass('mprm_ie_browser')) {
410
+ if (inner_height <= min_height) {
411
+ element.css('min-height', min_height);
412
+ } else {
413
+ element.css('min-height', inner_height);
414
+ }
415
+ } else {
416
+ /** IE block **/
417
+ inner_height = element.css('height', '').find('.mptt-inner-event-content').height();
418
+ element.height(elementHeight);
419
+
420
+ if (inner_height <= min_height) {
421
+ element.css('max-height', min_height);
422
+ } else {
423
+ element.css('height', '');
424
+ element.css('max-height', inner_height);
425
+ }
426
+ }
427
+
428
+ if (parent_height < elementHeight) {
429
+ element.height(elementHeight);
430
+ }
431
+ },
432
+ /**
433
+ * Recalculate Height
434
+ * @param tdParent
435
+ * @param element
436
+ */
437
+ recalculate_Height: function(tdParent, element) {
438
+ var events = $('.mptt-event-container', tdParent),
439
+ eventCount = events.length,
440
+ heightItem = 0,
441
+ top = 0,
442
+ tdHeight = tdParent.height();
443
+
444
+ if (!$('body').hasClass('mprm_ie_browser')) {
445
+
446
+ heightItem = 100 / ((eventCount > 0) ? eventCount : 1);
447
+
448
+ if (!_.isUndefined(element)) {
449
+ element.height(heightItem + '%');
450
+
451
+ } else {
452
+
453
+ $.each(events, function() {
454
+ var $event = $(this);
455
+ $event.height(heightItem + '%');
456
+
457
+ if (_.isEmpty($event.data('min-height'))) {
458
+ $event.data('min-height', $event.height());
459
+ }
460
+
461
+ $event.css('top', top + '%');
462
+ $event.removeClass('mptt-hidden');
463
+ top += heightItem;
464
+ });
465
+ }
466
+
467
+ } else {
468
+
469
+ heightItem = tdHeight / ((eventCount > 0) ? eventCount : 1);
470
+
471
+ if (!_.isUndefined(element)) {
472
+ element.height(heightItem);
473
+ } else {
474
+ $.each(events, function() {
475
+ var $event = $(this);
476
+ $event.height(heightItem);
477
+
478
+ if (_.isEmpty($event.data('min-height'))) {
479
+ var min_height = $event.height();
480
+ if (min_height === 0) {
481
+ $event.data('min-height', heightItem);
482
+ } else {
483
+ $event.data('min-height', min_height);
484
+ }
485
+ }
486
+
487
+ $event.css('top', top + 'px');
488
+ $event.removeClass('mptt-hidden');
489
+ top += heightItem;
490
+ });
491
+ }
492
+ }
493
+ },
494
+ /**
495
+ * Fill all possible height in ceil
496
+ */
497
+ setEventsHeight: function() {
498
+ var events = $('.mptt-shortcode-wrapper').find('table').find('td.event');
499
+ $.each(events, function() {
500
+ var td = $(this);
501
+ state.recalculate_Height(td);
502
+ });
503
+ },
504
+ /**
505
+ * Set user color settings
506
+ * @param selector
507
+ */
508
+ setColorSettings: function(selector) {
509
+ if (_.isUndefined(selector)) {
510
+ selector = '.mptt-colorized';
511
+ }
512
+
513
+ var elements = $(selector);
514
+ // var height = '';
515
+ $.each(elements, function() {
516
+ var element = $(this),
517
+ bg = element.attr('data-bg_hover_color'),
518
+ color = element.attr('data-hover_color'),
519
+ tdParent = element.parent();
520
+ // parentHeight = tdParent.height(),
521
+ // elementHeight = '';
522
+
523
+ switch (element.attr('data-type')) {
524
+ case "column":
525
+ case "event":
526
+ element.hover(
527
+ function() {
528
+ if (!_.isEmpty(bg)) {
529
+ element.css('background-color', bg);
530
+ }
531
+ if (!_.isEmpty(color)) {
532
+ element.css('color', color);
533
+ }
534
+
535
+ state.setEventHeight(element);
536
+
537
+ }, function() {
538
+ element.css('max-height', '').css('min-height', '');
539
+ state.recalculate_Height(tdParent, element);
540
+
541
+ element.css('background-color', element.attr('data-bg_color'));
542
+ element.css('color', element.attr('data-color'));
543
+ }
544
+ );
545
+ break;
546
+ case "widget":
547
+ element.hover(
548
+ function() {
549
+ element.css('background-color', element.attr('data-background-hover-color'));
550
+ element.css('color', $(this).attr('data-hover-color'));
551
+ element.css('border-left-color', element.attr('data-hover-border-color'));
552
+ },
553
+ function() {
554
+ element.css('background-color', element.attr('data-background-color'));
555
+ element.css('color', element.attr('data-color'));
556
+ element.css('border-left-color', element.attr('data-border-color'));
557
+ }
558
+ );
559
+ break;
560
+ default:
561
+ break;
562
+ }
563
+ });
564
+ },
565
+ /**
566
+ * Clear input data
567
+ */
568
+ clearTable: function() {
569
+ var $weekdayId = $("#weekday_id");
570
+ $('#add_event_table input:not(.button),#add_event_table textarea').val('');
571
+ $weekdayId.val($weekdayId.find('option:first').attr('value'));
572
+ },
573
+ /**
574
+ * get Row span
575
+ *
576
+ * @param events
577
+ * @returns {number}
578
+ */
579
+ getRowSpan: function(events) {
580
+ var arrMax = [];
581
+ var arrMin = [];
582
+
583
+ $.each(events, function(index) {
584
+ var start = $(this).attr('data-start');
585
+ var end = $(this).attr('data-end');
586
+ arrMin[index] = start;
587
+ arrMax[index] = end;
588
+ });
589
+ var min = Math.min.apply(Math, arrMin);
590
+ var max = Math.max.apply(Math, arrMax);
591
+
592
+ var rowSpan = (max - min);
593
+
594
+ return rowSpan < 1 ? 1 : rowSpan;
595
+ },
596
+ /**
597
+ * Responsive filter
598
+ *
599
+ * @param element
600
+ */
601
+ responsiveFilter: function(element) {
602
+ var eventID = 'all';
603
+ var parentShortcode = element.parents('.mptt-shortcode-wrapper');
604
+
605
+ if (element.is("select")) {
606
+ eventID = element.val();
607
+ } else {
608
+ eventID = element.attr('href').replace("#", "");
609
+ }
610
+
611
+ var $listEvent = parentShortcode.find('.mptt-list-event');
612
+
613
+ if (eventID !== 'all') {
614
+ $listEvent.hide();
615
+ parentShortcode.find('.mptt-list-event[data-event-id="' + eventID + '"]').show();
616
+ } else {
617
+ $listEvent.show();
618
+ }
619
+
620
+ $.each(parentShortcode.find('.mptt-column'), function() {
621
+ $(this).show();
622
+ if ($(this).find('.mptt-list-event:visible').length < 1) {
623
+ $(this).hide();
624
+ }
625
+ });
626
+
627
+ },
628
+ /**
629
+ * Filter static version
630
+ *
631
+ * @param element
632
+ */
633
+ filterStatic: function(element, event) {
634
+
635
+ var parentShortcode = element.parents('.mptt-shortcode-wrapper');
636
+ var eventID = '#all';
637
+ var id = _.isEmpty(parentShortcode.attr('id')) ? 'not-set' : parentShortcode.attr('id');
638
+
639
+ if (element.is("select")) {
640
+ eventID = element.val();
641
+ } else {
642
+ eventID = element.attr('href').replace("#", "");
643
+ }
644
+
645
+ if (event == false || event.originalEvent === undefined) {
646
+ //console.log('skip');
647
+ } else {
648
+ window.location.hash = id + ':' + eventID;
649
+ }
650
+
651
+ parentShortcode.find('table').hide();
652
+
653
+ parentShortcode.find('table[id="#' + eventID + '"]').fadeIn();
654
+
655
+ state.setEventsHeight();
656
+ },
657
+ /**
658
+ * Add class if exists events in <TD>
659
+ */
660
+ setClassTd: function() {
661
+ $.each($('.mptt-event-container'), function() {
662
+ $(this).parents('td').addClass('event');
663
+ });
664
+ },
665
+ /**
666
+ * Init TimeTable
667
+ */
668
+ initTableData: function() {
669
+ state.setClassTd();
670
+ state.setRowSpanTd();
671
+ var table_class = '.' + MPTT.table_class;
672
+
673
+ if ($(table_class).data('hide_empty_row')) {
674
+ state.hideEmptyRows();
675
+ }
676
+ },
677
+ /**
678
+ * init Filters
679
+ */
680
+ filterShortcodeEvents: function() {
681
+ var selector = $('.mptt-menu');
682
+
683
+ if (selector.length) {
684
+
685
+ selector.off('change').on('change', function(event) {
686
+ state.filterStatic($(this), event);
687
+ state.responsiveFilter($(this));
688
+ });
689
+
690
+ $('.mptt-navigation-tabs.mptt-menu a').off('click').on('click', function(event) {
691
+
692
+ var $currentTab = $(this);
693
+ $currentTab.parents('.mptt-navigation-tabs.mptt-menu').find('li').removeClass('active');
694
+
695
+ $currentTab.parents('li').addClass('active');
696
+ state.filterStatic($currentTab, event);
697
+ state.responsiveFilter($currentTab);
698
+
699
+ });
700
+
701
+ }
702
+ },
703
+ /**
704
+ * Show events in shortcode container by current event
705
+ * @param shortcode_wrapper
706
+ * @param event
707
+ */
708
+ showCurrentEvent: function(shortcode_wrapper, event) {
709
+ if (shortcode_wrapper.find('.mptt-menu').hasClass('mptt-navigation-tabs')) {
710
+ shortcode_wrapper.find('.mptt-navigation-tabs').find('a[href="#' + event + '"]').click();
711
+ } else if (shortcode_wrapper.find('.mptt-menu').hasClass('mptt-navigation-select')) {
712
+ if (shortcode_wrapper.find('.mptt-navigation-select option[value="' + event + '"]')) {
713
+ shortcode_wrapper.find('.mptt-navigation-select').val(event).change();
714
+ } else {
715
+ shortcode_wrapper.find('table[id="#all"]').fadeIn();
716
+ }
717
+ } else {
718
+ shortcode_wrapper.find('table[id="#all"]').fadeIn();
719
+ }
720
+ },
721
+ /**
722
+ * Filter by hash
723
+ */
724
+ getFilterByHash: function() {
725
+ var is_single = 1;
726
+ var hash = window.location.hash;
727
+
728
+ if (!_.isUndefined(hash)) {
729
+ var HashArray = hash.split(':');
730
+ var id = HashArray[0];
731
+ var event = HashArray[1];
732
+ var shortcode_wrapper = $('.mptt-shortcode-wrapper');
733
+ event = _.isUndefined(event) ? 'all' : event;
734
+
735
+ if (shortcode_wrapper.length === is_single) {
736
+ state.showCurrentEvent(shortcode_wrapper, event);
737
+ } else {
738
+
739
+ $.each(shortcode_wrapper, function(index, object) {
740
+ var element = $(object);
741
+ var element_id = '#' + element.attr('id');
742
+
743
+ if (element_id === id) {
744
+ state.showCurrentEvent(element, event);
745
+ } else {
746
+ state.showCurrentEvent(element, 'all');
747
+ }
748
+ });
749
+
750
+ }
751
+ }
752
+ state.setEventsHeight();
753
+ },
754
+ /**
755
+ * Clear table after change colSpan
756
+ *
757
+ * @param columnIndex
758
+ * @param toColSpan
759
+ * @param $table
760
+ * @param row
761
+ */
762
+ removeCellsAfterChangeColSpan: function(columnIndex, toColSpan, $table, row) {
763
+ for (columnIndex; columnIndex < toColSpan; columnIndex++) {
764
+ var columnId = $table.find('th[data-index="' + columnIndex + '"]').data('column-id');
765
+ row.find('td:not(.event)[data-column-id="' + columnId + '"]').remove();
766
+ }
767
+ },
768
+ /**
769
+ * Set rowSpan
770
+ * @param td
771
+ * @param rowSpan
772
+ * @param $table
773
+ * @param columnId
774
+ * @returns rowSpan
775
+ */
776
+ removeCellsAfterChangeRowSpan: function(td, rowSpan, $table, columnId) {
777
+ var index = td.parents('tr').attr('data-index'),
778
+ toRowSpan = rowSpan + parseInt(index) - 1,
779
+ colSpan = td.attr('colspan'),
780
+ columnIndex = $table.find('th[data-column-id="' + columnId + '"]').data('index'),
781
+ toColSpan = parseInt(columnIndex) + parseInt(colSpan);
782
+
783
+ for (index; index < toRowSpan; index++) {
784
+
785
+ var row = $table.find('tr.mptt-shortcode-row-' + (parseInt(index) + 1));
786
+
787
+ if (row.length) {
788
+
789
+ if (row.find('td.event[data-column-id="' + columnId + '"]').length) {
790
+ rowSpan -= (toRowSpan - index);
791
+
792
+ if (rowSpan < 2) {
793
+ rowSpan = 1;
794
+ break;
795
+ }
796
+ }
797
+
798
+ if (colSpan > 1) {
799
+ state.removeCellsAfterChangeColSpan(columnIndex, toColSpan, $table, row);
800
+ }
801
+
802
+ row.find('td:not(.event)[data-column-id="' + columnId + '"]').remove();
803
+ }
804
+ }
805
+ return rowSpan;
806
+ },
807
+ /**
808
+ * Set rowSpan td
809
+ */
810
+ setRowSpanTd: function() {
811
+ var table_class = '.' + MPTT.table_class;
812
+ $.each($(table_class), function() {
813
+ var $table = $(this);
814
+
815
+ $.each($table.find('td.event'), function() {
816
+ var td = $(this),
817
+ events = td.find('.mptt-event-container'),
818
+ columnId = td.attr('data-column-id'),
819
+ rowHeight = td.attr('data-row_height'),
820
+ rowSpan = state.getRowSpan(events);
821
+
822
+ if (!_.isUndefined(rowSpan) && rowSpan > 1) {
823
+
824
+ rowSpan = state.removeCellsAfterChangeRowSpan(td, rowSpan, $table, columnId);
825
+
826
+ if (!isNaN(rowHeight)) {
827
+ td.css('height', rowSpan * rowHeight);
828
+ }
829
+ }
830
+
831
+ td.attr('rowspan', rowSpan);
832
+ });
833
+ });
834
+ },
835
+ /**
836
+ * Remove empty rows
837
+ */
838
+ hideEmptyRows: function() {
839
+ var table_class = '.' + MPTT.table_class;
840
+
841
+ var trs = $(table_class + ' tbody tr'),
842
+ col_count = $(table_class).first().find('th').length;
843
+
844
+ $.each(trs, function(index, value) {
845
+ // if all columns in the row are empty
846
+ if ($(value).find('td.event').length === 0 && $(value).find('td').length === col_count) {
847
+ $(value).remove();
848
+ }
849
+ });
850
+ },
851
+ /**
852
+ * Widget settings
853
+ */
854
+ displaySettings: function() {
855
+ var $viewSettings = $('.view_settings');
856
+ if ($viewSettings.length) {
857
+ $viewSettings.change(function() {
858
+ if ($(this).val() === "all") {
859
+ var id = $(this).attr('id');
860
+ $(this).parents('.mptt-container').find('.next-days').css("display", "block");
861
+ }
862
+ else {
863
+ $(this).parents('.mptt-container').find(".next-days").css("display", "none");
864
+ }
865
+ });
866
+ }
867
+ },
868
+ /**
869
+ * Widget time settings
870
+ * @param selector
871
+ */
872
+ timeMode: function(selector) {
873
+ if (selector) {
874
+ var selector_id = "." + $(this).attr('id');
875
+ $('#' + selector).change(function() {
876
+ if ($(this).val() === "server") {
877
+ var id = $(this).attr('id');
878
+ $(this).parents('.mptt-container').find(selector_id).css("display", "block");
879
+ }
880
+ else {
881
+ $(this).parents('.mptt-container').find(selector_id).css("display", "none");
882
+ }
883
+ });
884
+ }
885
+ },
886
+ /**
887
+ * init DatePicker for column
888
+ */
889
+ initDatePicker: function() {
890
+ var $date_picker = $("#datepicker");
891
+
892
+ if ($date_picker.length) {
893
+ $date_picker.datepicker({
894
+ dateFormat: 'd/m/yy',
895
+ setDate: Date.parse($date_picker.val())
896
+ });
897
+ }
898
+ },
899
+ /**
900
+ * init Column radio box change
901
+ */
902
+ columnRadioBox: function() {
903
+ var $date_picker = $('#datepicker');
904
+ var $column_option = $('input.option-input[name="column[column_option]"]');
905
+ var $weekday = $('select.mp-weekday');
906
+
907
+ if ($column_option.length) {
908
+ $column_option.on('change', function() {
909
+ switch ($(this).val()) {
910
+ case 'simple':
911
+ $weekday.prop("disabled", true);
912
+ $date_picker.prop("disabled", true);
913
+ break;
914
+ case 'weekday':
915
+ $weekday.prop("disabled", false);
916
+ $date_picker.val('').prop("disabled", true);
917
+ break;
918
+ case 'date':
919
+ $weekday.prop("disabled", true);
920
+ $date_picker.prop("disabled", false);
921
+ break;
922
+ }
923
+ });
924
+ }
925
+ }
926
+ };
927
+ }
928
+
929
+ return {
930
+ getInstance: function() {
931
+ if (!state) {
932
+ state = createInstance();
933
+ }
934
+ return state;
935
+ }
936
+ };
937
+ })(jQuery));
media/js/events/event.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t){var e={};function n(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,n),a.l=!0,a.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)n.d(i,a,function(e){return t[e]}.bind(null,a));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=2)}({2:function(t,e){Registry.register("Event",function(t){"use strict";var e;return{getInstance:function(){return e||(e={event_id:"",eventsData:{},init:function(){e.initTimePicker(),e.addEventButton(),e.initDeleteButtons(),e.initEditButtons(),e.initColorPicker(),e.initDatePicker(),e.columnRadioBox()},initTimePicker:function(){var e=Boolean(parseInt(t("#time_format").val()));t("#event_start").timepicker({showPeriod:e,showPeriodLabels:e,defaultTime:"00:00"}),t("#event_end").timepicker({showPeriod:e,showPeriodLabels:e,defaultTime:"00:00"})},initSlider:function(n,i){var a=!_.isUndefined(i)&&Boolean(i),s=n.replace(/^\D+/g,"");t(n).carouFredSel({items:{visible:3},direction:"up",scroll:{items:1,easing:"swing",pauseOnHover:!0,onAfter:function(e){e.items.old.each(function(){t(this).removeClass("visible")}),e.items.visible.each(function(){t(this).addClass("visible")})}},auto:{play:a,timeoutDuration:3e3},prev:{button:"#mp_prev_button"+s},next:{button:"#mp_next_button"+s}}),t(n).trigger("currentVisible",function(t){t.addClass("visible")}),e.setColorSettings(n+" .mptt-colorized")},initDeleteButton:function(){var e=t("#events-list");e.find("li.event").find("i.operation-button.dashicons-no.dashicons").off("click").on("click",function(){e.find("li.event").length>1?t(this).parents("li.event").remove():e.remove()})},initColorPicker:function(e){_.isUndefined(e)&&(e="");var n=t(e+" input.clr-picker"),i=t(e+" input.regular-text");n.spectrum("destroy"),n.spectrum({preferredFormat:"rgb",showInput:!0,showAlpha:!0,allowEmpty:!0,palette:[["#000","#444","#666","#999","#ccc","#eee","#f3f3f3","#fff"],["#f00","#f90","#ff0","#0f0","#0ff","#00f","#90f","#f0f"],["#f4cccc","#fce5cd","#fff2cc","#d9ead3","#d0e0e3","#cfe2f3","#d9d2e9","#ead1dc"],["#ea9999","#f9cb9c","#ffe599","#b6d7a8","#a2c4c9","#9fc5e8","#b4a7d6","#d5a6bd"],["#e06666","#f6b26b","#ffd966","#93c47d","#76a5af","#6fa8dc","#8e7cc3","#c27ba0"],["#c00","#e69138","#f1c232","#6aa84f","#45818e","#3d85c6","#674ea7","#a64d79"],["#900","#b45f06","#bf9000","#38761d","#134f5c","#0b5394","#351c75","#741b47"],["#600","#783f04","#7f6000","#274e13","#0c343d","#073763","#20124d","#4c1130"]],showPalette:!0,show:function(e){t(this).val(e)},hide:function(e){t(this).parents(".select-color").find(".regular-text").val(t(this).val())},change:function(e){t(this).parents(".select-color").find('input:not([type="hidden"])').val(t(this).val())}}),i.off("keyup").on("keyup",function(){var e=t(this).parents(".select-color"),n=e.find(".clr-picker"),i=e.find(".regular-text").val();e.find(".sp-preview-inner").css({"background-color":i}),n.spectrum("set",i)})},addEventButton:function(){t(document).on("click.admin","#add_mp_event",function(){t(this).hasClass("edit")?e.updateEventData():e.renderEventItem()})},initDeleteButtons:function(){t(document).on("click.admin","#events-list .delete-event-button",function(){var n=t(this).attr("data-id");e.deleteEvent(n)})},initEditButtons:function(){t(document).on("click.admin","#events-list .edit-event-button",function(){var n=t(this).attr("data-id"),i=t(this).parent().parent();t(this).parent().find(".spinner").addClass("is-active"),Registry._get("adminFunctions").wpAjax({controller:"events",action:"get_event_data",id:n},function(n){var a=t("#add_mp_event"),s=t("#events-list");s.find(".spinner").removeClass("is-active"),s.find(" tr").removeClass("active"),i.addClass("active"),t("#event_start").val(n.event_start),t("#event_end").val(n.event_end),t("#description").val(n.description),t("#user_id").val(n.user_id),t("#weekday_id").val(n.column_id),a.addClass("edit"),a.val("Update"),e.event_id=n.id},function(t){console.warn(t)})})},deleteEvent:function(e){Registry._get("adminFunctions").wpAjax({controller:"events",action:"delete",id:e},function(n){var i=t("#events-list").find('tr[data-id="'+e+'"]');i.length&&i.remove()},function(t){console.log(t)})},updateEventItem:function(){var n=t("#events-list").find('tr[data-id="'+e.event_id+'"]'),i=t("#user_id");n.find("td.event-column").text(t("#weekday_id").find("option:selected").text()),n.find("td.event-start").text(t("#event_start").val()),n.find("td.event-end").text(t("#event_end").val()),n.find("td.event-user-id").text("-1"===i.val()?"":i.find("option:selected").text()),n.find("td.event-description").text(t("#description").val()),e.event_id=null,t("#add_mp_event").removeClass("edit").val("Add New")},updateEventData:function(){var n=t("#add_event_table").find(".spinner");n.addClass("is-active"),Registry._get("adminFunctions").wpAjax({controller:"events",action:"update_event_data",data:{id:Registry._get("Event").event_id,event_start:t("#event_start").val(),event_end:t("#event_end").val(),description:t("#description").val(),user_id:t("#user_id").val(),weekday_ids:t("#weekday_id").val()}},function(){n.removeClass("is-active"),e.updateEventItem(),e.clearTable()},function(t){n.removeClass("is-active"),console.log(t)})},renderEventItem:function(){var n=t("#weekday_id"),i=t("#user_id"),a=n.find("option:selected").val(),s=t("#event_start"),r=t("#event_end"),o=t("#description"),d={tag:"tr",attrs:{},content:[{tag:"td",attrs:{style:"display:none;"},content:[{tag:"input",attrs:{type:"hidden",name:"event_data["+a+"][weekday_ids][]",value:a}},{tag:"input",attrs:{type:"hidden",name:"event_data["+a+"][event_start][]",value:s.val()}},{tag:"input",attrs:{type:"hidden",name:"event_data["+a+"][event_end][]",value:r.val()}},{tag:"input",attrs:{type:"hidden",name:"event_data["+a+"][description][]",value:o.val()}},{tag:"input",attrs:{type:"hidden",name:"event_data["+a+"][user_id][]",value:i.val()}}]},{tag:"td",attrs:{class:"event-column"},content:[n.find("option:selected").text()]},{tag:"td",attrs:{class:"event-start"},content:[s.val()]},{tag:"td",attrs:{class:"event-end"},content:[r.val()]},{tag:"td",attrs:{class:"event-description"},content:[o.val()]},{tag:"td",attrs:{class:"event-user-id"},content:["-1"===i.val()?"":i.find("option:selected").text()]},{tag:"td",attrs:{},content:[]}]},c=Registry._get("adminFunctions").getHtml(d);t("#events-list").find("tbody").append(c),t(".events-list-wrapper").scrollTop(1e10),e.clearTable()},setEventHeight:function(e){var n=e.parent().outerHeight(),i=t("body"),a=e.height(),s=e.data("min-height"),r=e.find(".mptt-inner-event-content").height();e.css("position","").css("width","").css("min-height",""),i.hasClass("mprm_ie_browser")?(r=e.css("height","").find(".mptt-inner-event-content").height(),e.height(a),r<=s?e.css("max-height",s):(e.css("height",""),e.css("max-height",r))):r<=s?e.css("min-height",s):e.css("min-height",r),n<a&&e.height(a)},recalculate_Height:function(e,n){var i=t(".mptt-event-container",e),a=i.length,s=0,r=0,o=e.height();t("body").hasClass("mprm_ie_browser")?(s=o/(a>0?a:1),_.isUndefined(n)?t.each(i,function(){var e=t(this);if(e.height(s),_.isEmpty(e.data("min-height"))){var n=e.height();0===n?e.data("min-height",s):e.data("min-height",n)}e.css("top",r+"px"),e.removeClass("mptt-hidden"),r+=s}):n.height(s)):(s=100/(a>0?a:1),_.isUndefined(n)?t.each(i,function(){var e=t(this);e.height(s+"%"),_.isEmpty(e.data("min-height"))&&e.data("min-height",e.height()),e.css("top",r+"%"),e.removeClass("mptt-hidden"),r+=s}):n.height(s+"%"))},setEventsHeight:function(){var n=t(".mptt-shortcode-wrapper").find("table").find("td.event");t.each(n,function(){var n=t(this);e.recalculate_Height(n)})},setColorSettings:function(n){_.isUndefined(n)&&(n=".mptt-colorized");var i=t(n);t.each(i,function(){var n=t(this),i=n.attr("data-bg_hover_color"),a=n.attr("data-hover_color"),s=n.parent();switch(n.attr("data-type")){case"column":case"event":n.hover(function(){_.isEmpty(i)||n.css("background-color",i),_.isEmpty(a)||n.css("color",a),e.setEventHeight(n)},function(){n.css("max-height","").css("min-height",""),e.recalculate_Height(s,n),n.css("background-color",n.attr("data-bg_color")),n.css("color",n.attr("data-color"))});break;case"widget":n.hover(function(){n.css("background-color",n.attr("data-background-hover-color")),n.css("color",t(this).attr("data-hover-color")),n.css("border-left-color",n.attr("data-hover-border-color"))},function(){n.css("background-color",n.attr("data-background-color")),n.css("color",n.attr("data-color")),n.css("border-left-color",n.attr("data-border-color"))})}})},clearTable:function(){var e=t("#weekday_id");t("#add_event_table input:not(.button),#add_event_table textarea").val(""),e.val(e.find("option:first").attr("value"))},getRowSpan:function(e){var n=[],i=[];t.each(e,function(e){var a=t(this).attr("data-start"),s=t(this).attr("data-end");i[e]=a,n[e]=s});var a=Math.min.apply(Math,i),s=Math.max.apply(Math,n)-a;return s<1?1:s},responsiveFilter:function(e){var n="all",i=e.parents(".mptt-shortcode-wrapper");n=e.is("select")?e.val():e.attr("href").replace("#","");var a=i.find(".mptt-list-event");"all"!==n?(a.hide(),i.find('.mptt-list-event[data-event-id="'+n+'"]').show()):a.show(),t.each(i.find(".mptt-column"),function(){t(this).show(),t(this).find(".mptt-list-event:visible").length<1&&t(this).hide()})},filterStatic:function(t,n){var i=t.parents(".mptt-shortcode-wrapper"),a="#all",s=_.isEmpty(i.attr("id"))?"not-set":i.attr("id");a=t.is("select")?t.val():t.attr("href").replace("#",""),0==n||void 0===n.originalEvent||(window.location.hash=s+":"+a),i.find("table").hide(),i.find('table[id="#'+a+'"]').fadeIn(),e.setEventsHeight()},setClassTd:function(){t.each(t(".mptt-event-container"),function(){t(this).parents("td").addClass("event")})},initTableData:function(){e.setClassTd(),e.setRowSpanTd();var n="."+MPTT.table_class;t(n).data("hide_empty_row")&&e.hideEmptyRows()},filterShortcodeEvents:function(){var n=t(".mptt-menu");n.length&&(n.off("change").on("change",function(n){e.filterStatic(t(this),n),e.responsiveFilter(t(this))}),t(".mptt-navigation-tabs.mptt-menu a").off("click").on("click",function(n){var i=t(this);i.parents(".mptt-navigation-tabs.mptt-menu").find("li").removeClass("active"),i.parents("li").addClass("active"),e.filterStatic(i,n),e.responsiveFilter(i)}))},showCurrentEvent:function(t,e){t.find(".mptt-menu").hasClass("mptt-navigation-tabs")?t.find(".mptt-navigation-tabs").find('a[href="#'+e+'"]').click():t.find(".mptt-menu").hasClass("mptt-navigation-select")&&t.find('.mptt-navigation-select option[value="'+e+'"]')?t.find(".mptt-navigation-select").val(e).change():t.find('table[id="#all"]').fadeIn()},getFilterByHash:function(){var n=window.location.hash;if(!_.isUndefined(n)){var i=n.split(":"),a=i[0],s=i[1],r=t(".mptt-shortcode-wrapper");s=_.isUndefined(s)?"all":s,1===r.length?e.showCurrentEvent(r,s):t.each(r,function(n,i){var r=t(i);"#"+r.attr("id")===a?e.showCurrentEvent(r,s):e.showCurrentEvent(r,"all")})}e.setEventsHeight()},removeCellsAfterChangeColSpan:function(t,e,n,i){for(;t<e;t++){var a=n.find('th[data-index="'+t+'"]').data("column-id");i.find('td:not(.event)[data-column-id="'+a+'"]').remove()}},removeCellsAfterChangeRowSpan:function(t,n,i,a){for(var s=t.parents("tr").attr("data-index"),r=n+parseInt(s)-1,o=t.attr("colspan"),d=i.find('th[data-column-id="'+a+'"]').data("index"),c=parseInt(d)+parseInt(o);s<r;s++){var l=i.find("tr.mptt-shortcode-row-"+(parseInt(s)+1));if(l.length){if(l.find('td.event[data-column-id="'+a+'"]').length&&(n-=r-s)<2){n=1;break}o>1&&e.removeCellsAfterChangeColSpan(d,c,i,l),l.find('td:not(.event)[data-column-id="'+a+'"]').remove()}}return n},setRowSpanTd:function(){var n="."+MPTT.table_class;t.each(t(n),function(){var n=t(this);t.each(n.find("td.event"),function(){var i=t(this),a=i.find(".mptt-event-container"),s=i.attr("data-column-id"),r=i.attr("data-row_height"),o=e.getRowSpan(a);!_.isUndefined(o)&&o>1&&(o=e.removeCellsAfterChangeRowSpan(i,o,n,s),isNaN(r)||i.css("height",o*r)),i.attr("rowspan",o)})})},hideEmptyRows:function(){var e="."+MPTT.table_class,n=t(e+" tbody tr"),i=t(e).first().find("th").length;t.each(n,function(e,n){0===t(n).find("td.event").length&&t(n).find("td").length===i&&t(n).remove()})},displaySettings:function(){var e=t(".view_settings");e.length&&e.change(function(){"all"===t(this).val()?(t(this).attr("id"),t(this).parents(".mptt-container").find(".next-days").css("display","block")):t(this).parents(".mptt-container").find(".next-days").css("display","none")})},timeMode:function(e){if(e){var n="."+t(this).attr("id");t("#"+e).change(function(){"server"===t(this).val()?(t(this).attr("id"),t(this).parents(".mptt-container").find(n).css("display","block")):t(this).parents(".mptt-container").find(n).css("display","none")})}},initDatePicker:function(){var e=t("#datepicker");e.length&&e.datepicker({dateFormat:"d/m/yy",setDate:Date.parse(e.val())})},columnRadioBox:function(){var e=t("#datepicker"),n=t('input.option-input[name="column[column_option]"]'),i=t("select.mp-weekday");n.length&&n.on("change",function(){switch(t(this).val()){case"simple":i.prop("disabled",!0),e.prop("disabled",!0);break;case"weekday":i.prop("disabled",!1),e.val("").prop("disabled",!0);break;case"date":i.prop("disabled",!0),e.prop("disabled",!1)}})}}),e}}}(jQuery))}});
media/js/lib/jBox.js ADDED
@@ -0,0 +1,1574 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ ---
3
+ description: jBox is a powerful and flexible jQuery plugin, taking care of all your modal windows, tooltips, notices and more.
4
+
5
+ authors: Stephan Wagner (http://stephanwagner.me)
6
+
7
+ license: MIT (http://www.opensource.org/licenses/mit-license.php)
8
+
9
+ requires: jQuery 1.11.0 (http://code.jquery.com/jquery-1.11.0.min.js)
10
+ jQuery 2.1.0 (http://code.jquery.com/jquery-2.1.0.min.js)
11
+
12
+ documentation: http://stephanwagner.me/jBox/documentation
13
+
14
+ demos: http://stephanwagner.me/jBox/demos
15
+ ...
16
+ */
17
+
18
+ function jBox(type, options) {
19
+
20
+ this.options = {
21
+
22
+ // jBox ID
23
+ id: null, // Choose a unique id, otherwise jBox will set one for you (jBoxID1, jBoxID2, ...)
24
+
25
+ // Dimensions
26
+ width: 'auto', // Width of content area (e.g. 'auto', 100)
27
+ height: 'auto', // Height of content area
28
+ minWidth: null, // Minimum width
29
+ maxHeight: null, // Minimum height
30
+ minWidth: null, // Maximum width
31
+ maxHeight: null, // Minimum height
32
+
33
+ // Attach
34
+ attach: null, // Attach jBox to elements (if no target element is provided, jBox will use the attached element as target)
35
+ trigger: 'click', // The event to open or close your jBoxes, use 'click' or 'mouseenter'
36
+ preventDefault: false, // Prevent default event when opening jBox (e.g. don't follow the href in a link when clicking on it)
37
+
38
+ // Content
39
+ title: null, // Adds a title to your jBox
40
+ content: null, // You can use a string to set text or HTML as content, or an element selector (e.g. jQuery('#jBox-content')) to append one or several elements (elements appended will get style display: 'block', so hide them with CSS style display: 'none' beforehand)
41
+ getTitle: null, // Get the title from an attribute when jBox opens
42
+ getContent: null, // Get the content from an attribute when jBox opens
43
+
44
+ // AJAX request
45
+ ajax: { // Setting an url will make an AJAX call when jBox opens
46
+ url: null, // URL to send the AJAX request to
47
+ data: '', // Data to send with your AJAX call (e.g. 'id=82&limit=10')
48
+ // Optional you can add any jQuery AJAX option (http://api.jquery.com/jquery.ajax/)
49
+ reload: false, // Resend the ajax call every time jBox opens
50
+ getData: 'data-ajax', // The attribute in the source element where the AJAX will look for the data to send with, e.g. data-ajax="id=82&limit=10"
51
+ setContent: true, // Automatically set the response as new content when the AJAX call is finished
52
+ spinner: true // Hides the current content and adds a spinner while loading, you can pass html content to add your own spinner, e.g. spinner: '<div class="mySpinner"></div>'
53
+ },
54
+
55
+ // Position
56
+ target: null, // The target element where jBox will be opened
57
+ position: {
58
+ x: 'center', // Horizontal Position (Use a number, 'left', 'right' or 'center')
59
+ y: 'center' // Vertical Position (Use a number, 'top', 'bottom' or 'center')
60
+ },
61
+ outside: null, // Use 'x', 'y', or 'xy' to move your jBox outside of the target element
62
+ offset: 0, // Offset to final position, you can set different values for x and y with an object e.g. {x: 15, y: 0}
63
+
64
+ attributes: { // Note that attributes can only be 'left' or 'right' when using numbers for position, e.g. {x: 300, y: 20}
65
+ x: 'left', // Horizontal position, use 'left' or 'right'
66
+ y: 'top' // Vertical position, use 'top' or 'bottom'
67
+ },
68
+ adjustPosition: false, // Adjusts the position when there is not enough space (use true, 'flip' or 'move')
69
+ adjustTracker: false, // By default jBox adjusts the position when opening, to adjust when scrolling or resizing, use 'scroll', 'resize' or 'true' (both events)
70
+ adjustDistance: 5, // How far from the window edge we start adjusting, use an object to set different values: {bottom: 5, top: 50, left: 5, right: 20}
71
+ fixed: false, // Your jBox will stay on position when scrolling
72
+ reposition: false, // Calculates new position when the window-size changes
73
+ repositionOnOpen: true, // Calculates new position each time jBox opens (rather than only when it opens the first time)
74
+ repositionOnContent: true, // Calculates new position when the content changes with .setContent() or .setTitle()
75
+
76
+ // Pointer
77
+ pointer: false, // Your pointer will always point towards the target element, so the option outside should be 'x' or 'y'
78
+ pointTo: 'target', // Setting something else than 'target' will add a pointer even if there is no target element set or found (Use 'top', 'bottom', 'left' or 'right')
79
+
80
+ // Animations
81
+ fade: 180, // Fade duration in ms, set to 0 or false to disable
82
+ animation: null, // Animation when opening or closing (use 'pulse', 'zoomIn', 'zoomOut', 'move', 'slide', 'flip', 'tada') (CSS inspired from Daniel Edens Animate.css: http://daneden.me/animate)
83
+
84
+ // Appearance
85
+ theme: 'Default', // Set a jBox theme class
86
+ addClass: '', // Adds classes to the wrapper
87
+ overlay: false, // Adds an overlay when jBox opens (set color and opacity with CSS)
88
+ zIndex: 10000, // Use a high zIndex (your overlay will have the lowest zIndex of all your jBoxes (with overlays) minus one)
89
+
90
+ // Delays
91
+ delayOpen: 0, // Delay opening in ms (Note that the delay will be ignored if your jBox didn't finish closing)
92
+ delayClose: 0, // Delay closing in ms (Note that there is always a closing delay of at least 10ms to ensure jBox won't be closed when opening right away)
93
+
94
+ // Closing events
95
+ closeOnEsc: false, // Close jBox when pressing [esc] key
96
+ closeOnClick: false, // Close jBox with mouseclick, use 'true' (click anywhere), 'box' (click on jBox itself), 'overlay' (click on the overlay), 'body' (click anywhere but jBox)
97
+ closeOnMouseleave: false, // Close jBox when the mouse leaves the jBox area or the area of the attached element
98
+ closeButton: false, // Adds a close button to your jBox, use 'title', 'overlay', 'box' or true (true will add the button to overlay, title or box, in that order if any of those elements can be found)
99
+
100
+ // Other options
101
+ constructOnInit: false, // Construct jBox when it's being initialized
102
+ blockScroll: false, // When jBox is open, block scrolling
103
+ appendTo: jQuery('body'), // Provide an element if you want the jBox to be positioned inside a specific element (only useful for fixed positions or when position values are numbers)
104
+ draggable: null, // Make your jBox draggable (use 'true', 'title' or provide an element as handle) (inspired from Chris Coyiers CSS-Tricks http://css-tricks.com/snippets/jquery/draggable-without-jquery-ui/)
105
+ dragOver: true, // When you have multiple draggable jBoxes, the one you select will always move over the other ones
106
+
107
+ // Events // Note: You can use 'this' in the event functions, it refers to your jBox object (e.g. onInit: function() { this.open(); })
108
+ onInit: function() {}, // Triggered when jBox is initialized, just before it's being created
109
+ onCreated: function() {}, // Triggered when jBox is created and is availible in DOM
110
+ onOpen: function() {}, // Triggered when jBox is opened
111
+ onClose: function() {}, // Triggered when jBox is closed
112
+ onCloseComplete: function() {}, // Triggered when jBox is completely closed (when fading is finished, useful if you want to destroy the jBox when it is closed)
113
+
114
+ // Only for type "Confirm"
115
+ confirmButton: 'Submit', // Text for the submit button
116
+ cancelButton: 'Cancel', // Text for the cancel button
117
+ confirm: null, // Function to execute when clicking the submit button. By default jBox will use firstly the onclick and secondly the href attribute
118
+ cancel: null, // Function to execute when clicking the cancel button
119
+
120
+ // Only for type "Notice"
121
+ autoClose: 7000, // Time when jBox should close automatically
122
+ color: null, // Makes your notices colorful, use 'black', 'red', 'green', 'blue', 'yellow'
123
+ stack: true, // Set to false to disable notice-stacking
124
+ audio: false, // Set the url to an audio file without extention, e.g. '/url/filename'. jBox will look for an .mp3 and an .ogg file
125
+ volume: 100, // Percent of volume for audio files
126
+
127
+ // Only for type "Image"
128
+ src: 'href', // The attribute where jBox gets the image source from, e.g. href="/path_to_image/image.jpg"
129
+ gallery: 'data-jbox-image', // The attribute where you define the image gallery, e.g. data-jbox-image="gallery1"
130
+ imageLabel: 'title', // The attribute where jBox gets the image label from, e.g. title="My label"
131
+ imageFade: 600, // The fade duration for images
132
+ imageSize: 'contain' // How to display the images: Use CSS background-position values, e.g. 'cover', 'contain', 'auto', 'initial', '50% 50%'
133
+ };
134
+
135
+ // Default type options
136
+ this.defaultOptions = {
137
+ // Default options for tooltips
138
+ 'Tooltip': {
139
+ getContent: 'title',
140
+ trigger: 'mouseenter',
141
+ position: {x: 'center', y: 'top'},
142
+ outside: 'y',
143
+ pointer: true,
144
+ adjustPosition: true,
145
+ reposition: true
146
+ },
147
+ // Default options for mouse tooltips
148
+ 'Mouse': {
149
+ target: 'mouse',
150
+ position: {x: 'right', y: 'bottom'},
151
+ offset: 15,
152
+ trigger: 'mouseenter',
153
+ adjustPosition: 'flip'
154
+ },
155
+ // Default options for modal windows
156
+ 'Modal': {
157
+ target: jQuery(window),
158
+ fixed: true,
159
+ blockScroll: true,
160
+ closeOnEsc: true,
161
+ closeOnClick: 'overlay',
162
+ closeButton: true,
163
+ overlay: true,
164
+ animation: 'zoomOut'
165
+ },
166
+ // Default options for modal confirm windows
167
+ 'Confirm': {
168
+ target: jQuery(window),
169
+ fixed: true,
170
+ attach: jQuery('[data-confirm]'),
171
+ getContent: 'data-confirm',
172
+ content: 'Do you really want to do this?',
173
+ minWidth: 320,
174
+ maxWidth: 460,
175
+ blockScroll: true,
176
+ closeOnEsc: true,
177
+ closeOnClick: 'overlay',
178
+ closeButton: true,
179
+ overlay: true,
180
+ animation: 'zoomOut',
181
+ preventDefault: true,
182
+ _onAttach: function(el) {
183
+ // Extract the href or the onclick event if no submit event is passed
184
+ if (!this.options.confirm) {
185
+ var submit = el.attr('onclick') ? el.attr('onclick') : (el.attr('href') ? (el.attr('target') ? 'window.open("' + el.attr('href') + '", "' + el.attr('target') + '");' : 'window.location.href = "' + el.attr('href') + '";') : '');
186
+ el.prop('onclick', null).data('jBox-Confirm-submit', submit);
187
+ }
188
+ },
189
+ _onCreated: function() {
190
+ // Add a footer to the jBox container
191
+ this.footer = jQuery('<div class="jBox-Confirm-footer"/>');
192
+ jQuery('<div class="jBox-Confirm-button jBox-Confirm-button-cancel"/>').html(this.options.cancelButton).click(function() { this.options.cancel && this.options.cancel(); this.close(); }.bind(this)).appendTo(this.footer);
193
+ this.submitButton = jQuery('<div class="jBox-Confirm-button jBox-Confirm-button-submit"/>').html(this.options.confirmButton).appendTo(this.footer);
194
+ this.footer.appendTo(this.container);
195
+ },
196
+ _onOpen: function() {
197
+ // Set the new action for the submit button
198
+ this.submitButton.off('click.jBox-Confirm' + this.id).on('click.jBox-Confirm' + this.id, function() { this.options.confirm ? this.options.confirm() : eval(this.source.data('jBox-Confirm-submit')); this.close(); }.bind(this));
199
+ }
200
+ },
201
+ // Default options for notices
202
+ 'Notice': {
203
+ target: jQuery(window),
204
+ fixed: true,
205
+ position: {x: 20, y: 20},
206
+ attributes: {x: 'right', y: 'top'},
207
+ animation: 'zoomIn',
208
+ closeOnClick: 'box',
209
+ _onInit: function () {
210
+ this.open();
211
+ this.options.delayClose = this.options.autoClose;
212
+ this.options.delayClose && this.close();
213
+ },
214
+ _onCreated: function() {
215
+ this.options.color && this.wrapper.addClass('jBox-Notice-color jBox-Notice-' + this.options.color);
216
+ this.wrapper.data('jBox-Notice-position', this.options.attributes.x + '-' + this.options.attributes.y);
217
+ },
218
+ _onOpen: function() {
219
+ // Loop through notices at same window corner and either move or destroy them
220
+ jQuery.each(jQuery('.jBox-Notice'), function(index, el) {
221
+ el = jQuery(el);
222
+
223
+ if (el.attr('id') == this.id || el.data('jBox-Notice-position') != this.options.attributes.x + '-' + this.options.attributes.y) return;
224
+ if (!this.options.stack) {
225
+ el.data('jBox').close({ignoreDelay: true});
226
+ return;
227
+ }
228
+ el.css('margin-' + this.options.attributes.y, parseInt(el.css('margin-' + this.options.attributes.y)) + this.wrapper.outerHeight() + 10);
229
+ }.bind(this));
230
+
231
+ // Play audio file, IE8 doesn't support audio
232
+ this.options.audio && this.audio({url: this.options.audio, valume: this.options.volume});
233
+ },
234
+ // Remove notice from DOM when closing finishes
235
+ _onCloseComplete: function() {
236
+ this.destroy();
237
+ }
238
+ },
239
+ // Default options for images
240
+ 'Image': {
241
+ target: jQuery(window),
242
+ fixed: true,
243
+ blockScroll: true,
244
+ closeOnEsc: true,
245
+ closeOnClick: 'overlay',
246
+ closeButton: true,
247
+ overlay: true,
248
+ animation: 'zoomOut',
249
+ width: 800,
250
+ height: 533,
251
+ attach: jQuery('[data-jbox-image]'),
252
+ preventDefault: true,
253
+
254
+ // TODO: What if the image is not found?
255
+ // TODO: What if the first image of a gallery needs some time to load, but other images are in content. Maybe add a blank black container
256
+
257
+ _onInit: function() {
258
+ this.images = this.currentImage = {};
259
+ this.imageZIndex = 1;
260
+
261
+ // Loop through images, sort and save in global variable
262
+ this.attachedElements && jQuery.each(this.attachedElements, function (index, item) {
263
+ item = jQuery(item);
264
+ if (item.data('jBox-image-gallery')) return;
265
+ var gallery = item.attr(this.options.gallery) || 'default';
266
+ !this.images[gallery] && (this.images[gallery] = []);
267
+ this.images[gallery].push({src: item.attr(this.options.src), label: (item.attr(this.options.imageLabel) || '')});
268
+
269
+ // Remove the title attribute so it won't show the browsers tooltip
270
+ this.options.imageLabel == 'title' && item.removeAttr('title');
271
+
272
+ // Store data in source element for easy access
273
+ item.data('jBox-image-gallery', gallery);
274
+ item.data('jBox-image-id', (this.images[gallery].length - 1));
275
+ }.bind(this));
276
+
277
+ // Helper to inject the image into content area
278
+ var appendImage = function(gallery, id, preload, open) {
279
+ if (jQuery('#jBox-image-' + gallery + '-' + id).length) return;
280
+
281
+ var image = jQuery('<div/>', {
282
+ id: 'jBox-image-' + gallery + '-' + id,
283
+ 'class': 'jBox-image-container'
284
+ }).css({
285
+ backgroundImage: 'url(' + this.images[gallery][id].src + ')',
286
+ backgroundSize: this.options.imageSize,
287
+ opacity: (open ? 1 : 0),
288
+ zIndex: (preload ? 0 : this.imageZIndex++)
289
+ }).appendTo(this.content);
290
+
291
+ var text = jQuery('<div/>', {
292
+ id: 'jBox-image-label-' + gallery + '-' + id,
293
+ 'class': 'jBox-image-label' + (open ? ' active' : '')
294
+ }).html(this.images[gallery][id].label).appendTo(this.imageLabel);
295
+
296
+ !open && !preload && image.animate({opacity: 1}, this.options.imageFade);
297
+ }.bind(this);
298
+
299
+ // Helper to show new image label
300
+ var showLabel = function(gallery, id) {
301
+ jQuery('.jBox-image-label.active').removeClass('active');
302
+ jQuery('#jBox-image-label-' + gallery + '-' + id).addClass('active');
303
+ };
304
+
305
+ // Show images when they are loaded or load them if not
306
+ this.showImage = function(img) {
307
+ // Get the gallery and the image id from the next or the previous image
308
+ if (img != 'open') {
309
+ var gallery = this.currentImage.gallery;
310
+ var id = this.currentImage.id + (1 * (img == 'prev') ? -1 : 1);
311
+ id = id > (this.images[gallery].length - 1) ? 0 : (id < 0 ? (this.images[gallery].length - 1) : id);
312
+ // Or get image data from source element
313
+ } else {
314
+ var gallery = this.source.data('jBox-image-gallery');
315
+ var id = this.source.data('jBox-image-id');
316
+
317
+ // Remove or show the next and prev buttons
318
+ jQuery('.jBox-image-pointer-prev, .jBox-image-pointer-next').css({display: (this.images[gallery].length > 1 ? 'block' : 'none')});
319
+ }
320
+
321
+ // Set new current image
322
+ this.currentImage = {gallery: gallery, id: id};
323
+
324
+ // Show image if it already exists
325
+ if (jQuery('#jBox-image-' + gallery + '-' + id).length) {
326
+ jQuery('#jBox-image-' + gallery + '-' + id).css({zIndex: this.imageZIndex++, opacity: 0}).animate({opacity: 1}, (img == 'open') ? 0 : this.options.imageFade);
327
+ showLabel(gallery, id);
328
+
329
+ // Load image if not found
330
+ } else {
331
+ // TODO loading not working properly anymore
332
+ this.wrapper.addClass('jBox-loading');
333
+ var image = jQuery('<img src="' + this.images[gallery][id].src + '">').load(function() {
334
+ appendImage(gallery, id, false);
335
+ showLabel(gallery, id);
336
+ this.wrapper.removeClass('jBox-loading');
337
+ }.bind(this));
338
+ }
339
+
340
+ // Preload next image
341
+ var next_id = id + 1;
342
+ next_id = next_id > (this.images[gallery].length - 1) ? 0 : (next_id < 0 ? (this.images[gallery].length - 1) : next_id);
343
+
344
+ (!jQuery('#jBox-image-' + gallery + '-' + next_id).length) && jQuery('<img src="' + this.images[gallery][next_id].src + '">').load(function() {
345
+ appendImage(gallery, next_id, true);
346
+ });
347
+ };
348
+ },
349
+ _onCreated: function() {
350
+
351
+ // TODO: NO ID!!!
352
+
353
+ this.imageLabel = jQuery('<div/>', {'id': 'jBox-image-label'}).appendTo(this.wrapper);
354
+ this.wrapper.append(jQuery('<div/>', {'class': 'jBox-image-pointer-prev', click: function() { this.showImage('prev'); }.bind(this)})).append(jQuery('<div/>', {'class': 'jBox-image-pointer-next', click: function() { this.showImage('next'); }.bind(this)}));
355
+ },
356
+ _onOpen: function() {
357
+ // Add a class to body so you can control the appearance of the overlay, for images a darker one is better
358
+ jQuery('body').addClass('jBox-image-open');
359
+
360
+ // Add key events
361
+ jQuery(document).on('keyup.jBox-' + this.id, function(ev) {
362
+ (ev.keyCode == 37) && this.showImage('prev');
363
+ (ev.keyCode == 39) && this.showImage('next');
364
+ }.bind(this));
365
+
366
+ // Load the image from the attached element
367
+ this.showImage('open');
368
+ },
369
+ _onClose: function() {
370
+ jQuery('body').removeClass('jBox-image-open');
371
+
372
+ // Remove key events
373
+ jQuery(document).off('keyup.jBox-' + this.id);
374
+ },
375
+ _onCloseComplete: function() {
376
+ // Hide all images
377
+ this.wrapper.find('.jBox-image-container').css('opacity', 0);
378
+ }
379
+ }
380
+ };
381
+
382
+ // Set default options for jBox types
383
+ if (jQuery.type(type) == 'string') {
384
+ this.type = type;
385
+ type = this.defaultOptions[type];
386
+ }
387
+
388
+ // Merge options
389
+ this.options = jQuery.extend(true, this.options, type, options);
390
+
391
+ // Get unique ID
392
+ if (this.options.id === null) {
393
+ this.options.id = 'jBoxID' + jBox._getUniqueID();
394
+ }
395
+ this.id = this.options.id;
396
+
397
+ // Correct impossible options
398
+ ((this.options.position.x == 'center' && this.options.outside == 'x') || (this.options.position.y == 'center' && this.options.outside == 'y')) && (this.options.outside = false);
399
+ (!this.options.outside || this.options.outside == 'xy') && (this.options.pointer = false);
400
+
401
+ // Correct multiple choice options
402
+ jQuery.type(this.options.offset) != 'object' && (this.options.offset = {x: this.options.offset, y: this.options.offset});
403
+ this.options.offset.x || (this.options.offset.x = 0);
404
+ this.options.offset.y || (this.options.offset.y = 0);
405
+ jQuery.type(this.options.adjustDistance) != 'object' ? (this.options.adjustDistance = {top: this.options.adjustDistance, right: this.options.adjustDistance, bottom: this.options.adjustDistance, left: this.options.adjustDistance}) : (this.options.adjustDistance = jQuery.extend({top: 5, left: 5, right: 5, bottom: 5}, this.options.adjustDistance));
406
+
407
+ // Save where the jBox is aligned to
408
+ this.align = (this.options.outside && this.options.outside != 'xy') ? this.options.position[this.options.outside] : (this.options.position.y != 'center' && jQuery.type(this.options.position.y) != 'number' ? this.options.position.x : (this.options.position.x != 'center' && jQuery.type(this.options.position.x) != 'number' ? this.options.position.y : this.options.attributes.x));
409
+
410
+ // Save default outside position
411
+ this.options.outside && this.options.outside != 'xy' && (this.outside = this.options.position[this.options.outside]);
412
+
413
+ // I know browser detection is bad practice, but for now it seems the only option to get jBox working in IE8
414
+ var userAgent = navigator.userAgent.toLowerCase();
415
+ this.IE8 = userAgent.indexOf('msie') != -1 && parseInt(userAgent.split('msie')[1]) == 8;
416
+
417
+ // Save global var for webkit prefix
418
+ this.prefix = userAgent.indexOf('webkit') != -1 ? '-webkit-' : '';
419
+
420
+ // Internal functions, used to easily get values
421
+ this._getOpp = function(opp) { return {left: 'right', right: 'left', top: 'bottom', bottom: 'top', x: 'y', y: 'x'}[opp]; };
422
+ this._getXY = function(xy) { return {left: 'x', right: 'x', top: 'y', bottom: 'y', center: 'x'}[xy]; };
423
+ this._getTL = function(tl) { return {left: 'left', right: 'left', top: 'top', bottom: 'top', center: 'left', x: 'left', y: 'top'}[tl]; };
424
+
425
+ // Check for SVG support
426
+ this._supportsSVG = function() {
427
+ return document.createElement('svg').getAttributeNS;
428
+ }
429
+
430
+ // Create an svg element
431
+ this._createSVG = function(type, options) {
432
+ var svg = document.createElementNS('http://www.w3.org/2000/svg', type);
433
+ jQuery.each(options, function (index, item) {
434
+ svg.setAttribute(item[0], (item[1] || ''));
435
+ });
436
+ return svg;
437
+ };
438
+
439
+ // Append a svg element to a svg container
440
+ this._appendSVG = function(source, target) {
441
+ return target.appendChild(source);
442
+ };
443
+
444
+ // Create jBox
445
+ this._create = function() {
446
+ if (this.wrapper) return;
447
+
448
+ // Create wrapper
449
+ this.wrapper = jQuery('<div/>', {
450
+ id: this.id,
451
+ 'class': 'jBox-wrapper' + (this.type ? ' jBox-' + this.type : '') + (this.options.theme ? ' jBox-' + this.options.theme : '') + (this.options.addClass ? ' ' + this.options.addClass : '') + (this.IE8 ? ' jBox-IE8' : '')
452
+ }).css({
453
+ position: (this.options.fixed ? 'fixed' : 'absolute'),
454
+ display: 'none',
455
+ opacity: 0,
456
+ zIndex: this.options.zIndex
457
+
458
+ // Save the jBox instance in the wrapper, so you can get access to your jBox when you only have the element
459
+ }).data('jBox', this);
460
+
461
+ // Add mouseleave event (.parents('*') might be a performance nightmare! Maybe there is a better way)
462
+ this.options.closeOnMouseleave && this.wrapper.mouseleave(function(ev) {
463
+ // Only close when the new target is not the source element
464
+ !this.source || !(ev.relatedTarget == this.source[0] || jQuery.inArray(this.source[0], jQuery(ev.relatedTarget).parents('*')) !== -1) && this.close();
465
+ }.bind(this));
466
+
467
+ // Add closeOnClick: 'box' events
468
+ (this.options.closeOnClick == 'box') && this.wrapper.on('touchend click', function() { this.close({ignoreDelay: true}); }.bind(this));
469
+
470
+ // Create container
471
+ this.container = jQuery('<div/>', {'class': 'jBox-container'}).appendTo(this.wrapper);
472
+
473
+ // Create content
474
+ this.content = jQuery('<div/>', {'class': 'jBox-content'}).css({width: this.options.width, height: this.options.height, minWidth: this.options.minWidth, minHeight: this.options.minHeight, maxWidth: this.options.maxWidth, maxHeight: this.options.maxHeight}).appendTo(this.container);
475
+
476
+ // Create close button
477
+ if (this.options.closeButton) {
478
+ this.closeButton = jQuery('<div/>', {'class': 'jBox-closeButton jBox-noDrag'}).on('touchend click', function(ev) { this.isOpen && this.close({ignoreDelay: true}); }.bind(this));
479
+
480
+ if (this._supportsSVG()) {
481
+ var closeButtonSVG = this._createSVG('svg', [['viewBox', '0 0 24 24']]);
482
+ this._appendSVG(this._createSVG('path', [['d', 'M22.2,4c0,0,0.5,0.6,0,1.1l-6.8,6.8l6.9,6.9c0.5,0.5,0,1.1,0,1.1L20,22.3c0,0-0.6,0.5-1.1,0L12,15.4l-6.9,6.9c-0.5,0.5-1.1,0-1.1,0L1.7,20c0,0-0.5-0.6,0-1.1L8.6,12L1.7,5.1C1.2,4.6,1.7,4,1.7,4L4,1.7c0,0,0.6-0.5,1.1,0L12,8.5l6.8-6.8c0.5-0.5,1.1,0,1.1,0L22.2,4z']]), closeButtonSVG);
483
+ this.closeButton.append(closeButtonSVG);
484
+ } else {
485
+ this.wrapper.addClass('jBox-nosvg');
486
+ }
487
+
488
+ // Add close button to jBox container
489
+ if (this.options.closeButton == 'box' || (this.options.closeButton === true && !this.options.overlay && !this.options.title)) {
490
+ this.wrapper.addClass('jBox-closeButton-box');
491
+ this.closeButton.appendTo(this.container);
492
+ }
493
+ }
494
+
495
+ // Append jBox to DOM
496
+ this.wrapper.appendTo(this.options.appendTo);
497
+
498
+ // Create pointer
499
+ if (this.options.pointer) {
500
+
501
+ // Get pointer vars and save globally
502
+ this.pointer = {
503
+ position: (this.options.pointTo != 'target') ? this.options.pointTo : this._getOpp(this.outside),
504
+ xy: (this.options.pointTo != 'target') ? this._getXY(this.options.pointTo) : this._getXY(this.outside),
505
+ align: 'center',
506
+ offset: 0
507
+ };
508
+
509
+ this.pointer.element = jQuery('<div/>', {'class': 'jBox-pointer jBox-pointer-' + this.pointer.position}).appendTo(this.wrapper);
510
+ this.pointer.dimensions = {
511
+ x: this.pointer.element.outerWidth(),
512
+ y: this.pointer.element.outerHeight()
513
+ };
514
+
515
+ if (jQuery.type(this.options.pointer) == 'string') {
516
+ var split = this.options.pointer.split(':');
517
+ split[0] && (this.pointer.align = split[0]);
518
+ split[1] && (this.pointer.offset = parseInt(split[1]));
519
+ }
520
+ this.pointer.alignAttribute = (this.pointer.xy == 'x' ? (this.pointer.align == 'bottom' ? 'bottom' : 'top') : (this.pointer.align == 'right' ? 'right' : 'left'));
521
+
522
+ // Set wrapper CSS
523
+ this.wrapper.css('padding-' + this.pointer.position, this.pointer.dimensions[this.pointer.xy]);
524
+
525
+ // Set pointer CSS
526
+ this.pointer.element.css(this.pointer.alignAttribute, (this.pointer.align == 'center' ? '50%' : 0)).css('margin-' + this.pointer.alignAttribute, this.pointer.offset);
527
+ this.pointer.margin = {}; this.pointer.margin['margin-' + this.pointer.alignAttribute] = this.pointer.offset;
528
+
529
+ // Add a transform to fix centered position
530
+ (this.pointer.align == 'center') && this.pointer.element.css(this.prefix + 'transform', 'translate(' + (this.pointer.xy == 'y' ? (this.pointer.dimensions.x * -0.5 + 'px') : 0) + ', ' + (this.pointer.xy == 'x' ? (this.pointer.dimensions.y * -0.5 + 'px') : 0) + ')');
531
+
532
+ this.pointer.element.css((this.pointer.xy == 'x' ? 'width' : 'height'), parseInt(this.pointer.dimensions[this.pointer.xy]) + parseInt(this.container.css('border-' + this.pointer.alignAttribute + '-width')));
533
+
534
+ // Add class to wrapper for CSS access
535
+ this.wrapper.addClass('jBox-pointerPosition-' + this.pointer.position);
536
+ }
537
+
538
+ // Set title and content
539
+ this.setContent(this.options.content, true);
540
+ this.setTitle(this.options.title, true);
541
+
542
+ // Make jBox draggable
543
+ if (this.options.draggable) {
544
+ var handle = (this.options.draggable == 'title') ? this.titleContainer : (this.options.draggable.length > 0 ? this.options.draggable : (this.options.draggable.selector ? jQuery(this.options.draggable.selector) : this.wrapper));
545
+ handle.addClass('jBox-draggable').on('mousedown', function(ev) {
546
+ if (ev.button == 2 || jQuery(ev.target).hasClass('jBox-noDrag') || jQuery(ev.target).parents('.jBox-noDrag').length) return;
547
+
548
+ if (this.options.dragOver && this.wrapper.css('zIndex') <= jBox.zIndexMax) {
549
+ jBox.zIndexMax += 1;
550
+ this.wrapper.css('zIndex', jBox.zIndexMax);
551
+ }
552
+
553
+ var drg_h = this.wrapper.outerHeight(),
554
+ drg_w = this.wrapper.outerWidth(),
555
+ pos_y = this.wrapper.offset().top + drg_h - ev.pageY,
556
+ pos_x = this.wrapper.offset().left + drg_w - ev.pageX;
557
+ jQuery(document).on('mousemove.jBox-draggable-' + this.id, function(ev) {
558
+ this.wrapper.offset({
559
+ top: ev.pageY + pos_y - drg_h,
560
+ left: ev.pageX + pos_x - drg_w
561
+ });
562
+ }.bind(this));
563
+ ev.preventDefault();
564
+ }.bind(this)).on('mouseup', function() { jQuery(document).off('mousemove.jBox-draggable-' + this.id); }.bind(this));
565
+
566
+ // Add z-index
567
+ jBox.zIndexMax = !jBox.zIndexMax ? this.options.zIndex : Math.max(jBox.zIndexMax, this.options.zIndex);
568
+ }
569
+
570
+ // Fire onCreated event
571
+ (this.options.onCreated.bind(this))();
572
+ this.options._onCreated && (this.options._onCreated.bind(this))();
573
+ };
574
+
575
+ // Create jBox onInit
576
+ this.options.constructOnInit && this._create();
577
+
578
+ // Attach jBox
579
+ this.options.attach && this.attach();
580
+
581
+ // Position jBox on mouse
582
+ this._positionMouse = function(ev) {
583
+
584
+ // Calculate positions
585
+ this.pos = {
586
+ left: ev.pageX,
587
+ top: ev.pageY
588
+ };
589
+ var setPosition = function(a, p) {
590
+ // Set centered position
591
+ if (this.options.position[p] == 'center') {
592
+ this.pos[a] -= Math.ceil(this.dimensions[p] / 2);
593
+ return;
594
+ }
595
+ // Move to left or top
596
+ this.pos[a] += (a == this.options.position[p]) ? ((this.dimensions[p] * -1) - this.options.offset[p]) : this.options.offset[p];
597
+
598
+ return this.pos[a];
599
+ }.bind(this);
600
+
601
+ // Set position to wrapper
602
+ this.wrapper.css({
603
+ left: setPosition('left', 'x'),
604
+ top: setPosition('top', 'y')
605
+ });
606
+
607
+ // Adjust mouse position
608
+ this.targetDimensions = {x: 0, y: 0, left: ev.pageX, top: ev.pageY};
609
+ this._adjustPosition();
610
+ };
611
+
612
+ // Attach document and window events
613
+ this._attachEvents = function() {
614
+
615
+ // Closing event: closeOnEsc
616
+ this.options.closeOnEsc && jQuery(document).on('keyup.jBox-' + this.id, function(ev) { if (ev.keyCode == 27) { this.close({ignoreDelay: true}); }}.bind(this));
617
+
618
+ // Closing event: closeOnClick
619
+ if (this.options.closeOnClick === true || this.options.closeOnClick == 'body') {
620
+ jQuery(document).on('touchend.jBox-' + this.id + ' click.jBox-' + this.id, function(ev) {
621
+ if (this.blockBodyClick || (this.options.closeOnClick == 'body' && (ev.target == this.wrapper[0] || this.wrapper.has(ev.target).length)))
622
+ return;
623
+ this.close({ignoreDelay: true});
624
+ }.bind(this));
625
+ }
626
+
627
+ // Positioning events
628
+ if (((this.options.adjustPosition && this.options.adjustTracker) || this.options.reposition) && !this.fixed && this.outside) {
629
+
630
+ var scrollTimer,
631
+ scrollTimerTriggered = 0,
632
+ scrollTriggerDelay = 150; // Trigger scroll and resize events every 150 ms (set a higher value to improve performance)
633
+
634
+ // Function to delay positioning event
635
+ var positionDelay = function () {
636
+ var now = new Date().getTime();
637
+ if (!scrollTimer) {
638
+ if (now - scrollTimerTriggered > scrollTriggerDelay) {
639
+ this.options.reposition && this.position();
640
+ this.options.adjustTracker && this._adjustPosition();
641
+ scrollTimerTriggered = now;
642
+ }
643
+ scrollTimer = setTimeout(function() {
644
+ scrollTimer = null;
645
+ scrollTimerTriggered = new Date().getTime();
646
+ this.options.reposition && this.position();
647
+ this.options.adjustTracker && this._adjustPosition();
648
+ }.bind(this), scrollTriggerDelay);
649
+ }
650
+ }.bind(this);
651
+
652
+ // Trigger position events when scrolling
653
+ (this.options.adjustTracker && this.options.adjustTracker != 'resize') && jQuery(window).on('scroll.jBox-' + this.id, function(ev) { positionDelay(); }.bind(this));
654
+
655
+ // Trigger position events when resizing
656
+ ((this.options.adjustTracker && this.options.adjustTracker != 'scroll') || this.options.reposition) && jQuery(window).on('resize.jBox-' + this.id, function(ev) { positionDelay(); }.bind(this));
657
+ }
658
+
659
+ // Mousemove events
660
+ this.options.target == 'mouse' && jQuery('body').on('mousemove.jBox-' + this.id, function(ev) { this._positionMouse(ev); }.bind(this));
661
+ };
662
+
663
+ // Detach document and window events
664
+ this._detachEvents = function() {
665
+
666
+ // Closing event: closeOnEsc
667
+ this.options.closeOnEsc && jQuery(document).off('keyup.jBox-' + this.id);
668
+
669
+ // Closing event: closeOnClick
670
+ (this.options.closeOnClick === true || this.options.closeOnClick == 'body') && jQuery(document).off('touchend.jBox-' + this.id + ' click.jBox-' + this.id);
671
+
672
+ // Positioning events
673
+ if ((this.options.adjustPosition && this.options.adjustTracker) || this.options.reposition) {
674
+ jQuery(window).off('scroll.jBox-' + this.id);
675
+ jQuery(window).off('resize.jBox-' + this.id);
676
+ }
677
+
678
+ // Mousemove events
679
+ this.options.target == 'mouse' && jQuery('body').off('mousemove.jBox-' + this.id);
680
+ };
681
+
682
+ // Add overlay
683
+ this._addOverlay = function() {
684
+
685
+ // If the overlay isn't cached, set overlay or create it
686
+ if (!this.overlay) {
687
+ // Get the overlay and adjust z-Index
688
+ this.overlay = jQuery('#jBox-overlay').length ? jQuery('#jBox-overlay').css({zIndex: Math.min(jQuery('#jBox-overlay').css('z-index'), (this.options.zIndex - 1))}) : (jQuery('<div/>', {id: 'jBox-overlay'}).css({display: 'none', opacity: 0, zIndex: (this.options.zIndex - 1)}).appendTo(jQuery('body')));
689
+
690
+ // Add close button to overlay
691
+ (this.options.closeButton == 'overlay' || this.options.closeButton === true) && ((jQuery('#jBox-overlay .jBox-closeButton').length > 0) ? jQuery('#jBox-overlay .jBox-closeButton').on('touchend click', function() { this.isOpen && this.close({ignoreDelay: true}); }.bind(this)) : this.overlay.append(this.closeButton));
692
+
693
+ // Add closeOnClick: 'overlay' events
694
+ (this.options.closeOnClick == 'overlay') && this.overlay.on('touchend click', function() { this.isOpen && this.close({ignoreDelay: true}); }.bind(this));
695
+ }
696
+
697
+ // Add jBox to overlay data
698
+ var overlay_data = this.overlay.data('jBox') || {};
699
+ overlay_data['jBox-' + this.id] = true;
700
+ this.overlay.data('jBox', overlay_data);
701
+
702
+ // Abort if overlay is shown already
703
+ if (this.overlay.css('display') == 'block') return;
704
+
705
+ // Show overlay
706
+ this.options.fade ? (this.overlay.stop() && this.overlay.animate({opacity: 1}, {
707
+ queue: false,
708
+ duration: this.options.fade,
709
+ start: function() { this.overlay.css({display: 'block'}); }.bind(this)
710
+ })) : this.overlay.css({display: 'block', opacity: 1});
711
+ };
712
+
713
+ // Remove overlay
714
+ this._removeOverlay = function() {
715
+
716
+ // Abort if no overlay found
717
+ if (!this.overlay) return;
718
+
719
+ // Remove jBox from data
720
+ var overlay_data = this.overlay.data('jBox');
721
+ delete overlay_data['jBox-' + this.id];
722
+ this.overlay.data('jBox', overlay_data);
723
+
724
+ // Hide overlay if no other jBox needs it
725
+ if (jQuery.isEmptyObject(overlay_data)) {
726
+ this.options.fade ? (this.overlay.stop() && this.overlay.animate({opacity: 0}, {
727
+ queue: false,
728
+ duration: this.options.fade,
729
+ complete: function() { this.overlay.css({display: 'none'}); }.bind(this)
730
+ })) : this.overlay.css({display: 'none', opacity: 0});
731
+ }
732
+ };
733
+
734
+ // Generate CSS for animations and append to header
735
+ this._generateCSS = function() {
736
+ if (this.IE8) return;
737
+
738
+ // Get open and close animations if none provided
739
+ (jQuery.type(this.options.animation) != 'object') && (this.options.animation = {
740
+ pulse: {open: 'pulse', close: 'zoomOut'},
741
+ zoomIn: {open: 'zoomIn', close: 'zoomIn'},
742
+ zoomOut: {open: 'zoomOut', close: 'zoomOut'},
743
+ move: {open: 'move', close: 'move'},
744
+ slide: {open: 'slide', close: 'slide'},
745
+ flip: {open: 'flip', close: 'flip'},
746
+ tada: {open: 'tada', close: 'zoomOut'}
747
+ }[this.options.animation]);
748
+
749
+ // Get direction var
750
+ this.options.animation.open && (this.options.animation.open = this.options.animation.open.split(':'));
751
+ this.options.animation.close && (this.options.animation.close = this.options.animation.close.split(':'));
752
+ this.options.animation.openDirection = this.options.animation.open ? this.options.animation.open[1] : null;
753
+ this.options.animation.closeDirection = this.options.animation.close ? this.options.animation.close[1] : null;
754
+ this.options.animation.open && (this.options.animation.open = this.options.animation.open[0]);
755
+ this.options.animation.close && (this.options.animation.close = this.options.animation.close[0]);
756
+
757
+ // Add 'Open' and 'Close' to animation names
758
+ this.options.animation.open && (this.options.animation.open += 'Open');
759
+ this.options.animation.close && (this.options.animation.close += 'Close');
760
+
761
+ // All animations
762
+ var animations = {
763
+ pulse: {
764
+ duration: 350,
765
+ css: [['0%', 'scale(1)'], ['50%', 'scale(1.1)'], ['100%', 'scale(1)']]
766
+ },
767
+ zoomInOpen: {
768
+ duration: (this.options.fade || 180),
769
+ css: [['0%', 'scale(0.9)'], ['100%', 'scale(1)']]
770
+ },
771
+ zoomInClose: {
772
+ duration: (this.options.fade || 180),
773
+ css: [['0%', 'scale(1)'], ['100%', 'scale(0.9)']]
774
+ },
775
+ zoomOutOpen: {
776
+ duration: (this.options.fade || 180),
777
+ css: [['0%', 'scale(1.1)'], ['100%', 'scale(1)']]
778
+ },
779
+ zoomOutClose: {
780
+ duration: (this.options.fade || 180),
781
+ css: [['0%', 'scale(1)'], ['100%', 'scale(1.1)']]
782
+ },
783
+ moveOpen: {
784
+ duration: (this.options.fade || 180),
785
+ positions: {top: {'0%': -12}, right: {'0%': 12}, bottom: {'0%': 12}, left: {'0%': -12}},
786
+ css: [['0%', 'translate%XY(%Vpx)'], ['100%', 'translate%XY(0px)']]
787
+ },
788
+ moveClose: {
789
+ duration: (this.options.fade || 180),
790
+ timing: 'ease-in',
791
+ positions: {top: {'100%': -12}, right: {'100%': 12}, bottom: {'100%': 12}, left: {'100%': -12}},
792
+ css: [['0%', 'translate%XY(0px)'], ['100%', 'translate%XY(%Vpx)']]
793
+ },
794
+ slideOpen: {
795
+ duration: 400,
796
+ positions: {top: {'0%': -400}, right: {'0%': 400}, bottom: {'0%': 400}, left: {'0%': -400}},
797
+ css: [['0%', 'translate%XY(%Vpx)'], ['100%', 'translate%XY(0px)']]
798
+ },
799
+ slideClose: {
800
+ duration: 400,
801
+ timing: 'ease-in',
802
+ positions: {top: {'100%': -400}, right: {'100%': 400}, bottom: {'100%': 400}, left: {'100%': -400}},
803
+ css: [['0%', 'translate%XY(0px)'], ['100%', 'translate%XY(%Vpx)']]
804
+ },
805
+ flipOpen: {
806
+ duration: 600,
807
+ css: [['0%', 'perspective(400px) rotateX(90deg)'], ['40%', 'perspective(400px) rotateX(-15deg)'], ['70%', 'perspective(400px) rotateX(15deg)'], ['100%', 'perspective(400px) rotateX(0deg)']]
808
+ },
809
+ flipClose: {
810
+ duration: (this.options.fade || 300),
811
+ css: [['0%', 'perspective(400px) rotateX(0deg)'], ['100%', 'perspective(400px) rotateX(90deg)']]
812
+ },
813
+ tada: {
814
+ duration: 800,
815
+ css: [['0%', 'scale(1)'], ['10%, 20%', 'scale(0.9) rotate(-3deg)'], ['30%, 50%, 70%, 90%', 'scale(1.1) rotate(3deg)'], ['40%, 60%, 80%', 'scale(1.1) rotate(-3deg)'], ['100%', 'scale(1) rotate(0)']]
816
+ }
817
+ };
818
+
819
+ // Set Open and Close names for standalone animations
820
+ jQuery.each(['pulse', 'tada'], function(index, item) { animations[item + 'Open'] = animations[item + 'Close'] = animations[item]; });
821
+
822
+ // Function to generate the CSS for the keyframes
823
+ var generateKeyframeCSS = function(ev, position) {
824
+
825
+ // Generate keyframes CSS
826
+ keyframe_css = '@' + this.prefix + 'keyframes jBox-animation-' + this.options.animation[ev] + '-' + ev + (position ? '-' + position : '') + ' {';
827
+ jQuery.each(animations[this.options.animation[ev]].css, function(index, item) {
828
+ var translate = position ? item[1].replace('%XY', this._getXY(position).toUpperCase()) : item[1];
829
+ animations[this.options.animation[ev]].positions && (translate = translate.replace('%V', animations[this.options.animation[ev]].positions[position][item[0]]));
830
+ keyframe_css += item[0] + ' {' + this.prefix + 'transform:' + translate + ';}';
831
+ }.bind(this));
832
+ keyframe_css += '}';
833
+
834
+ // Generate class CSS
835
+ keyframe_css += '.jBox-animation-' + this.options.animation[ev] + '-' + ev + (position ? '-' + position : '') + ' {';
836
+ keyframe_css += this.prefix + 'animation-duration: ' + animations[this.options.animation[ev]].duration + 'ms;';
837
+ keyframe_css += this.prefix + 'animation-name: jBox-animation-' + this.options.animation[ev] + '-' + ev + (position ? '-' + position : '') + ';';
838
+ keyframe_css += animations[this.options.animation[ev]].timing ? (this.prefix + 'animation-timing-function: ' + animations[this.options.animation[ev]].timing + ';') : '';
839
+ keyframe_css += '}';
840
+
841
+ return keyframe_css;
842
+ }.bind(this);
843
+
844
+ // Generate css for each event and positions
845
+ var css = '';
846
+ jQuery.each(['open', 'close'], function(index, ev) {
847
+ // No CSS needed for closing with no fade
848
+ if (!this.options.animation[ev] || !animations[this.options.animation[ev]] || (ev == 'close' && !this.options.fade)) return '';
849
+
850
+ // Generate CSS
851
+ animations[this.options.animation[ev]].positions ?
852
+ jQuery.each(['top', 'right', 'bottom', 'left'], function(index2, position) { css += generateKeyframeCSS(ev, position); }) :
853
+ css += generateKeyframeCSS(ev);
854
+ }.bind(this));
855
+
856
+ jQuery('<style/>').append(css).appendTo(jQuery('head'));
857
+ };
858
+
859
+ // Block body clicks for 10ms to prevent extra event triggering
860
+ this._blockBodyClick = function() {
861
+ this.blockBodyClick = true;
862
+ setTimeout(function() { this.blockBodyClick = false; }.bind(this), 10);
863
+ };
864
+
865
+ // Add css for animations
866
+ this.options.animation && this._generateCSS();
867
+
868
+ // Animations
869
+ this._animate = function(ev) {
870
+ if (this.IE8) return;
871
+ ev || (ev = this.isOpen ? 'open' : 'close');
872
+
873
+ // Don't animate when closing with no fade duration
874
+ if (!this.options.fade && ev == 'close') return null;
875
+
876
+ // Get the current position, use opposite if jBox is flipped
877
+ var animationDirection = (this.options.animation[ev + 'Direction'] || ((this.align != 'center') ? this.align : this.options.attributes.x));
878
+ this.flipped && this._getXY(animationDirection) == (this._getXY(this.align)) && (animationDirection = this._getOpp(animationDirection));
879
+
880
+ // Add event and position classes
881
+ var classnames = 'jBox-animation-' + this.options.animation[ev] + '-' + ev + ' jBox-animation-' + this.options.animation[ev] + '-' + ev + '-' + animationDirection;
882
+ this.wrapper.addClass(classnames);
883
+
884
+ // Get duration of animation
885
+ var animationDuration = parseFloat(this.wrapper.css(this.prefix + 'animation-duration')) * 1000;
886
+ ev == 'close' && (animationDuration = Math.min(animationDuration, this.options.fade));
887
+
888
+ // Remove animation classes when animation is finished
889
+ setTimeout(function() { this.wrapper.removeClass(classnames); }.bind(this), animationDuration);
890
+ };
891
+
892
+ // Abort animation
893
+ this._abortAnimation = function() {
894
+ if (this.IE8) return;
895
+
896
+ // Remove all animation classes
897
+ var prefix = 'jBox-animation';
898
+ var classes = this.wrapper.attr('class').split(' ').filter(function(c) {
899
+ return c.lastIndexOf(prefix, 0) !== 0;
900
+ });
901
+ this.wrapper.attr('class', classes.join(' '));
902
+ };
903
+
904
+ // Adjust position
905
+ this._adjustPosition = function() {
906
+ if (!this.options.adjustPosition) return null;
907
+
908
+ // Reset cached pointer position
909
+ if (this.positionAdjusted) {
910
+ this.wrapper.css(this.pos);
911
+ this.pointer && this.wrapper.css('padding', 0).css('padding-' + this._getOpp(this.outside), this.pointer.dimensions[this._getXY(this.outside)]).removeClass('jBox-pointerPosition-' + this._getOpp(this.pointer.position)).addClass('jBox-pointerPosition-' + this.pointer.position);
912
+ this.pointer && this.pointer.element.attr('class', 'jBox-pointer jBox-pointer-' + this._getOpp(this.outside)).css(this.pointer.margin);
913
+ this.positionAdjusted = false;
914
+ this.flipped = false;
915
+ }
916
+
917
+ // Get the window dimensions
918
+ var win = jQuery(window);
919
+ var windowDimensions = {
920
+ x: win.width(),
921
+ y: win.height(),
922
+ top: (this.options.fixed && this.target.data('jBox-fixed') ? 0 : win.scrollTop()),
923
+ left: (this.options.fixed && this.target.data('jBox-fixed') ? 0 : win.scrollLeft())
924
+ };
925
+ windowDimensions.bottom = windowDimensions.top + windowDimensions.y;
926
+ windowDimensions.right = windowDimensions.left + windowDimensions.x;
927
+
928
+ // Find out where the jBox is out of view area
929
+ var outYT = (windowDimensions.top > this.pos.top - (this.options.adjustDistance.top || 0)),
930
+ outXR = (windowDimensions.right < this.pos.left + this.dimensions.x + (this.options.adjustDistance.right || 0)),
931
+ outYB = (windowDimensions.bottom < this.pos.top + this.dimensions.y + (this.options.adjustDistance.bottom || 0)),
932
+ outXL = (windowDimensions.left > this.pos.left - (this.options.adjustDistance.left || 0)),
933
+ outX = outXL ? 'left' : (outXR ? 'right' : null),
934
+ outY = outYT ? 'top' : (outYB ? 'bottom' : null),
935
+ out = outX || outY;
936
+
937
+ // Stop here if jBox is not out of view area
938
+ if (!out) return;
939
+
940
+ // Flip jBox
941
+ if (this.options.adjustPosition != 'move' && (outX == this.outside || outY == this.outside)) {
942
+
943
+ this.target == 'mouse' && (this.outside = 'right');
944
+
945
+ // Check if enough space is availible on opposite position
946
+ if (((this.outside == 'top' || this.outside == 'left') ?
947
+ (windowDimensions[this._getXY(this.outside)] - (this.targetDimensions[this._getTL(this.outside)] - windowDimensions[this._getTL(this.outside)]) - this.targetDimensions[this._getXY(this.outside)]) + this.options.offset[this._getXY(this.outside)] :
948
+ (this.targetDimensions[this._getTL(this.outside)] - windowDimensions[this._getTL(this.outside)]) - this.options.offset[this._getXY(this.outside)]
949
+ ) > this.dimensions[this._getXY(this.outside)] + parseInt(this.options.adjustDistance[this._getOpp(this.outside)])) {
950
+
951
+ // Adjust wrapper and pointer
952
+ this.wrapper.css(this._getTL(this.outside), this.pos[this._getTL(this.outside)] + ((this.dimensions[this._getXY(this.outside)] + (this.options.offset[this._getXY(this.outside)] * (this.outside == 'top' || this.outside == 'left' ? -2 : 2)) + this.targetDimensions[this._getXY(this.outside)]) * (this.outside == 'top' || this.outside == 'left' ? 1 : -1)));
953
+ this.pointer && this.wrapper.removeClass('jBox-pointerPosition-' + this.pointer.position).addClass('jBox-pointerPosition-' + this._getOpp(this.pointer.position)).css('padding', 0).css('padding-' + this.outside, this.pointer.dimensions[this._getXY(this.outside)]);
954
+ this.pointer && this.pointer.element.attr('class', 'jBox-pointer jBox-pointer-' + this.outside);
955
+ this.positionAdjusted = true;
956
+ this.flipped = true;
957
+ }
958
+ }
959
+
960
+ // Move jBox (only possible with pointer)
961
+ var outMove = (this._getXY(this.outside) == 'x') ? outY : outX;
962
+
963
+ if (this.pointer && this.options.adjustPosition != 'flip' && this._getXY(outMove) == this._getOpp(this._getXY(this.outside))) {
964
+
965
+ // Get the maximum space we have availible to adjust
966
+ if (this.pointer.align == 'center') {
967
+ var spaceAvail = (this.dimensions[this._getXY(outMove)] / 2) - (this.pointer.dimensions[this._getOpp(this.pointer.xy)] / 2) - (parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) * (outMove != this._getTL(outMove) ? -1 : 1));
968
+ } else {
969
+ var spaceAvail = (outMove == this.pointer.alignAttribute) ?
970
+ parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) :
971
+ this.dimensions[this._getXY(outMove)] - parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) - this.pointer.dimensions[this._getXY(outMove)];
972
+ }
973
+
974
+ // Get the overlapping space
975
+ spaceDiff = (outMove == this._getTL(outMove)) ?
976
+ windowDimensions[this._getTL(outMove)] - this.pos[this._getTL(outMove)] + this.options.adjustDistance[outMove] :
977
+ (windowDimensions[this._getOpp(this._getTL(outMove))] - this.pos[this._getTL(outMove)] - this.options.adjustDistance[outMove] - this.dimensions[this._getXY(outMove)]) * -1;
978
+
979
+ // Add overlapping space on left or top window edge
980
+ if (outMove == this._getOpp(this._getTL(outMove)) && this.pos[this._getTL(outMove)] - spaceDiff < windowDimensions[this._getTL(outMove)] + this.options.adjustDistance[this._getTL(outMove)]) {
981
+ spaceDiff -= windowDimensions[this._getTL(outMove)] + this.options.adjustDistance[this._getTL(outMove)] - (this.pos[this._getTL(outMove)] - spaceDiff);
982
+ }
983
+
984
+ // Only adjust the maximum availible
985
+ spaceDiff = Math.min(spaceDiff, spaceAvail);
986
+
987
+ // Move jBox
988
+ if (spaceDiff <= spaceAvail && spaceDiff > 0) {
989
+ this.pointer.element.css('margin-' + this.pointer.alignAttribute, parseInt(this.pointer.element.css('margin-' + this.pointer.alignAttribute)) - (spaceDiff * (outMove != this.pointer.alignAttribute ? -1 : 1)));
990
+ this.wrapper.css(this._getTL(outMove), this.pos[this._getTL(outMove)] + (spaceDiff * (outMove != this._getTL(outMove) ? -1 : 1)));
991
+ this.positionAdjusted = true;
992
+ }
993
+ }
994
+ };
995
+
996
+ // Fire onInit event
997
+ (this.options.onInit.bind(this))();
998
+ this.options._onInit && (this.options._onInit.bind(this))();
999
+
1000
+ return this;
1001
+ };
1002
+
1003
+ // Attach jBox to elements
1004
+ jBox.prototype.attach = function(elements, trigger) {
1005
+ elements || (elements = jQuery(this.options.attach.selector || this.options.attach));
1006
+ trigger || (trigger = this.options.trigger);
1007
+
1008
+ elements && elements.length && jQuery.each(elements, function(index, el) {
1009
+ el = jQuery(el);
1010
+ if (!el.data('jBox-attached-' + this.id)) {
1011
+
1012
+ // Remove title attribute and store content on element
1013
+ (this.options.getContent == 'title' && el.attr('title') != undefined) && el.data('jBox-getContent', el.attr('title')).removeAttr('title');
1014
+
1015
+ // Add Element to collection
1016
+ this.attachedElements || (this.attachedElements = []);
1017
+ this.attachedElements.push(el[0]);
1018
+
1019
+ // Add click or mouseenter event, click events can prevent default as well
1020
+ el.on(trigger + '.jBox-attach-' + this.id, function(ev) {
1021
+ // Clear timer
1022
+ this.timer && clearTimeout(this.timer);
1023
+
1024
+ // Block opening when jbox is open and the source element is triggering
1025
+ if (trigger == 'mouseenter' && this.isOpen && this.source[0] == el[0])
1026
+ return;
1027
+
1028
+ // Only close jBox if you click the current target element, otherwise open at new target
1029
+ if (this.isOpen && this.source && this.source[0] != el[0]) var forceOpen = true;
1030
+
1031
+ // Set new source element
1032
+ this.source = el;
1033
+
1034
+ // Set new target
1035
+ !this.options.target && (this.target = el);
1036
+
1037
+ // Prevent default action on click
1038
+ trigger == 'click' && this.options.preventDefault && ev.preventDefault();
1039
+
1040
+ // Toggle or open jBox
1041
+ this[trigger == 'click' && !forceOpen ? 'toggle' : 'open']();
1042
+ }.bind(this));
1043
+
1044
+ // Add close event for trigger event mouseenter
1045
+ (this.options.trigger == 'mouseenter') && el.on('mouseleave', function(ev) {
1046
+ // If we have set closeOnMouseleave, do not close jBox when leaving attached element and mouse is over jBox
1047
+ if(!this.options.closeOnMouseleave || !(ev.relatedTarget == this.wrapper[0] || jQuery(ev.relatedTarget).parents('#' + this.id).length)) this.close();
1048
+ }.bind(this));
1049
+
1050
+ el.data('jBox-attached-' + this.id, trigger);
1051
+
1052
+ // TODO // TODO TOO CLOSE
1053
+ // Fire onAttach event
1054
+ this.options._onAttach && (this.options._onAttach.bind(this))(el);
1055
+ }
1056
+ }.bind(this));
1057
+
1058
+ return this;
1059
+ };
1060
+
1061
+ // Detach jBox from elements
1062
+ jBox.prototype.detach = function(elements) {
1063
+ elements || (elements = this.attachedElements || []);
1064
+
1065
+ elements && elements.length && jQuery.each(elements, function(index, el) {
1066
+ el = jQuery(el);
1067
+ // Remove events
1068
+ if (el.data('jBox-attached-' + this.id)) {
1069
+ el.off(el.data('jBox-attached-' + this.id) + '.jBox-attach-' + this.id);
1070
+ el.data('jBox-attached-' + this.id, null);
1071
+ }
1072
+ // Remove element from collection
1073
+ this.attachedElements = jQuery.grep(this.attachedElements, function(value) {
1074
+ return value != el[0];
1075
+ });
1076
+ }.bind(this));
1077
+
1078
+ return this;
1079
+ };
1080
+
1081
+ // Set title
1082
+ jBox.prototype.setTitle = function(title, ignore_positioning) {
1083
+ var wrapperHeight = this.wrapper.height(), wrapperWidth = this.wrapper.width();
1084
+ if (title == null || title == undefined) return this;
1085
+ !this.wrapper && this._create();
1086
+ if (!this.title) {
1087
+ this.titleContainer = jQuery('<div/>', {'class': 'jBox-title'});
1088
+ this.title = jQuery('<div/>').appendTo(this.titleContainer);
1089
+ this.wrapper.addClass('jBox-hasTitle');
1090
+ if (this.options.closeButton == 'title' || (this.options.closeButton === true && !this.options.overlay)) {
1091
+ this.wrapper.addClass('jBox-closeButton-title');
1092
+ this.closeButton.appendTo(this.titleContainer);
1093
+ }
1094
+ this.titleContainer.insertBefore(this.content);
1095
+ }
1096
+ this.title.html(title);
1097
+
1098
+ // Reposition if dimensions changed
1099
+ !ignore_positioning && this.options.repositionOnContent && (wrapperHeight != this.wrapper.height() || wrapperWidth != this.wrapper.width()) && this.position();
1100
+
1101
+ return this;
1102
+ };
1103
+
1104
+ // Set content
1105
+ jBox.prototype.setContent = function(content, ignore_positioning) {
1106
+ if (content == null) return this;
1107
+
1108
+ // Create jBox if no wrapper found
1109
+ !this.wrapper && this._create();
1110
+
1111
+ // Get the width and height of wrapper, only if they change we need to reposition
1112
+ var wrapperHeight = this.wrapper.height(), wrapperWidth = this.wrapper.width();
1113
+
1114
+ // Get the width and height of body, if they change with new content, adjust accordingly (happens when a hidden scrollbar changes body dimensions)
1115
+ var bodyHeight = jQuery('body').height(), bodyWidth = jQuery('body').width();
1116
+
1117
+ // Extract all appended containers to body
1118
+ this.content.children('[data-jbox-content-appended]').appendTo('body').css({display: 'none'});
1119
+
1120
+ // Set the new content
1121
+ switch (jQuery.type(content)) {
1122
+ case 'string': this.content.html(content); break;
1123
+ case 'object': this.content.html(''); content.attr('data-jbox-content-appended', 1).appendTo(this.content).css({display: 'block'}); break;
1124
+ }
1125
+
1126
+ // Calculate the difference to before the content was set
1127
+ var adjustOffset = {
1128
+ x: bodyWidth - jQuery('body').width(),
1129
+ y: bodyHeight - jQuery('body').height()
1130
+ };
1131
+
1132
+ // Reposition if dimensions changed
1133
+ !ignore_positioning && this.options.repositionOnContent && (wrapperHeight != this.wrapper.height() || wrapperWidth != this.wrapper.width()) && this.position({adjustOffset: adjustOffset});
1134
+
1135
+ return this;
1136
+ };
1137
+
1138
+ // Set new dimensions
1139
+ jBox.prototype.setDimensions = function(type, val, pos) {
1140
+
1141
+ // Create jBox if no wrapper found
1142
+ !this.wrapper && this._create();
1143
+
1144
+ // Default value is 'auto'
1145
+ val == undefined && (val == 'auto');
1146
+
1147
+ // Set CSS of content
1148
+ this.content.css(type, val);
1149
+
1150
+ // Reposition by default
1151
+ (pos == undefined || pos) && this.position();
1152
+ };
1153
+
1154
+ // Set width or height
1155
+ jBox.prototype.setWidth = function(val, pos) { this.setDimensions('width', val, pos); };
1156
+ jBox.prototype.setHeight = function(val, pos) { this.setDimensions('height', val, pos); };
1157
+
1158
+ // Position jBox
1159
+ jBox.prototype.position = function(options) {
1160
+ options || (options = {});
1161
+
1162
+ // Get target
1163
+ this.target = options.target || this.target || this.options.target || jQuery(window);
1164
+
1165
+ // Cache total current dimensions of jBox
1166
+ this.dimensions = {
1167
+ x: this.wrapper.outerWidth(),
1168
+ y: this.wrapper.outerHeight()
1169
+ };
1170
+
1171
+ // Mousemove can't be positioned
1172
+ if (this.target == 'mouse') return;
1173
+
1174
+ // Set percent and margin for centered inside
1175
+ if (this.options.position.x == 'center' && this.options.position.y == 'center') {
1176
+ this.wrapper.css({left: '50%', top: '50%', marginLeft: (this.dimensions.x * -0.5 + this.options.offset.x), marginTop: (this.dimensions.y * -0.5 + this.options.offset.y)});
1177
+ return this;
1178
+ }
1179
+
1180
+ // Total current dimensions of target element
1181
+ var targetOffset = this.target.offset();
1182
+
1183
+ // Add fixed data to target
1184
+ !this.target.data('jBox-fixed') && this.target.data('jBox-fixed', (this.target[0] != jQuery(window)[0] && (this.target.css('position') == 'fixed' || this.target.parents().filter(function() { return jQuery(this).css('position') == 'fixed'; }).length > 0)) ? 'fixed' : 'static');
1185
+
1186
+ // When the target is fixed and jBox is fixed, remove scroll offset
1187
+ if (this.target.data('jBox-fixed') == 'fixed' && this.options.fixed) {
1188
+ targetOffset.top = targetOffset.top - jQuery(window).scrollTop();
1189
+ targetOffset.left = targetOffset.left - jQuery(window).scrollLeft();
1190
+ }
1191
+
1192
+ // Store target dimensions
1193
+ this.targetDimensions = {
1194
+ x: this.target.outerWidth(),
1195
+ y: this.target.outerHeight(),
1196
+ top: (targetOffset ? targetOffset.top : 0),
1197
+ left: (targetOffset ? targetOffset.left : 0)
1198
+ };
1199
+ this.pos = {};
1200
+
1201
+ // Calculate positions
1202
+ var setPosition = function(p) {
1203
+
1204
+ // Set number positions
1205
+ if (jQuery.inArray(this.options.position[p], ['top', 'right', 'bottom', 'left', 'center']) == -1) {
1206
+ this.pos[this.options.attributes[p]] = this.options.position[p];
1207
+ return;
1208
+ }
1209
+
1210
+ // We have a target, so use 'left' or 'top' as attributes
1211
+ var a = this.options.attributes[p] = (p == 'x' ? 'left' : 'top');
1212
+
1213
+ // Start at target position
1214
+ this.pos[a] = this.targetDimensions[a];
1215
+
1216
+ // Set centered position
1217
+ if (this.options.position[p] == 'center') {
1218
+ this.pos[a] += Math.ceil((this.targetDimensions[p] - this.dimensions[p]) / 2);
1219
+ return;
1220
+ }
1221
+
1222
+ // Move inside
1223
+ (a != this.options.position[p]) && (this.pos[a] += this.targetDimensions[p] - this.dimensions[p]);
1224
+
1225
+ // Move outside
1226
+ (this.options.outside == p || this.options.outside == 'xy') && (this.pos[a] += this.dimensions[p] * (a != this.options.position[p] ? 1 : -1));
1227
+
1228
+ }.bind(this);
1229
+
1230
+ // Set position including offset
1231
+ setPosition('x');
1232
+ setPosition('y');
1233
+
1234
+ // Adjust position depending on pointer align
1235
+ if (this.options.pointer && jQuery.type(this.options.position.x) != 'number' && jQuery.type(this.options.position.y) != 'number') {
1236
+
1237
+ var adjustWrapper = 0;
1238
+
1239
+ // Where is the pointer aligned? Add or substract accordingly
1240
+ switch (this.pointer.align) {
1241
+ case 'center':
1242
+ if (this.options.position[this._getOpp(this.options.outside)] != 'center') {
1243
+ adjustWrapper += (this.dimensions[this._getOpp(this.options.outside)] / 2);
1244
+ }
1245
+ break;
1246
+ default:
1247
+ switch (this.options.position[this._getOpp(this.options.outside)]) {
1248
+ case 'center':
1249
+ adjustWrapper += ((this.dimensions[this._getOpp(this.options.outside)] / 2) - (this.pointer.dimensions[this._getOpp(this.options.outside)] / 2)) * (this.pointer.align == this._getTL(this.pointer.align) ? 1 : -1);
1250
+ break;
1251
+ default:
1252
+ adjustWrapper += (this.pointer.align != this.options.position[this._getOpp(this.options.outside)]) ?
1253
+
1254
+ // If pointer align is different to position align
1255
+ (this.dimensions[this._getOpp(this.options.outside)] * (jQuery.inArray(this.pointer.align, ['top', 'left']) !== -1 ? 1 : -1)) + ((this.pointer.dimensions[this._getOpp(this.options.outside)] / 2) * (jQuery.inArray(this.pointer.align, ['top', 'left']) !== -1 ? -1 : 1)) :
1256
+
1257
+ // If pointer align is same as position align
1258
+ (this.pointer.dimensions[this._getOpp(this.options.outside)] / 2) * (jQuery.inArray(this.pointer.align, ['top', 'left']) !== -1 ? 1 : -1);
1259
+ break;
1260
+ }
1261
+ break;
1262
+ }
1263
+ adjustWrapper *= (this.options.position[this._getOpp(this.options.outside)] == this.pointer.alignAttribute ? -1 : 1);
1264
+ adjustWrapper += this.pointer.offset * (this.pointer.align == this._getOpp(this._getTL(this.pointer.align)) ? 1 : -1);
1265
+
1266
+ this.pos[this._getTL(this._getOpp(this.pointer.xy))] += adjustWrapper;
1267
+ }
1268
+
1269
+ // Add adjustments
1270
+ options.adjustOffset && options.adjustOffset.x && (this.pos[this.options.attributes.x] += parseInt(options.adjustOffset.x) * (this.options.attributes.x == 'left' ? 1 : -1));
1271
+ options.adjustOffset && options.adjustOffset.y && (this.pos[this.options.attributes.y] += parseInt(options.adjustOffset.y) * (this.options.attributes.y == 'top' ? 1 : -1));
1272
+
1273
+ // Add final offset
1274
+ this.pos[this.options.attributes.x] += this.options.offset.x;
1275
+ this.pos[this.options.attributes.y] += this.options.offset.y;
1276
+
1277
+ // Set CSS
1278
+ this.wrapper.css(this.pos);
1279
+
1280
+ // Adjust position
1281
+ this._adjustPosition();
1282
+
1283
+ return this;
1284
+ };
1285
+
1286
+ // Open jBox
1287
+ jBox.prototype.open = function(options) {
1288
+ options || (options = {});
1289
+
1290
+ // Abort if jBox was destroyed
1291
+ if (this.isDestroyed) return false;
1292
+
1293
+ // Construct jBox if not already constructed
1294
+ !this.wrapper && this._create();
1295
+
1296
+ // Abort any opening or closing timer
1297
+ this.timer && clearTimeout(this.timer);
1298
+
1299
+ // Block body click for 10ms, so jBox can open on attached elements while closeOnClick = 'body'
1300
+ this._blockBodyClick();
1301
+
1302
+ // Block opening
1303
+ if (this.isDisabled) return this;
1304
+
1305
+ // Opening function
1306
+ var open = function() {
1307
+
1308
+ // Set title from source element
1309
+ this.source && this.options.getTitle && (this.source.attr(this.options.getTitle) && this.setTitle(this.source.attr(this.options.getTitle)), true);
1310
+
1311
+ // Set content from source element
1312
+ this.source && this.options.getContent && (this.source.data('jBox-getContent') ? this.setContent(this.source.data('jBox-getContent'), true) : (this.source.attr(this.options.getContent) ? this.setContent(this.source.attr(this.options.getContent), true) : null));
1313
+
1314
+ // Fire onOpen event
1315
+ (this.options.onOpen.bind(this))();
1316
+ this.options._onOpen && (this.options._onOpen.bind(this))();
1317
+
1318
+ // Get content from ajax
1319
+ ((this.options.ajax && this.options.ajax.url && (!this.ajaxLoaded || this.options.ajax.reload)) || (options.ajax && options.ajax.url)) && this.ajax(options.ajax || null);
1320
+
1321
+ // Set position
1322
+ (!this.positionedOnOpen || this.options.repositionOnOpen) && this.position({target: options.target}) && (this.positionedOnOpen = true);
1323
+
1324
+ // Abort closing
1325
+ this.isClosing && this._abortAnimation();
1326
+
1327
+ // Open functions to call when jBox is closed
1328
+ if (!this.isOpen) {
1329
+
1330
+ // jBox is open now
1331
+ this.isOpen = true;
1332
+
1333
+ // Attach events
1334
+ this._attachEvents();
1335
+
1336
+ // Block scrolling
1337
+ this.options.blockScroll && jQuery('body').addClass('jBox-blockScroll-' + this.id);
1338
+
1339
+ // Add overlay
1340
+ this.options.overlay && this._addOverlay();
1341
+
1342
+ // Only animate if jBox is compleately closed
1343
+ this.options.animation && !this.isClosing && this._animate('open');
1344
+
1345
+ // Fading animation or show immediately
1346
+ if (this.options.fade) {
1347
+ this.wrapper.stop().animate({opacity: 1}, {
1348
+ queue: false,
1349
+ duration: this.options.fade,
1350
+ start: function() {
1351
+ this.isOpening = true;
1352
+ this.wrapper.css({display: 'block'});
1353
+ }.bind(this),
1354
+ always: function() {
1355
+ this.isOpening = false;
1356
+ }.bind(this)
1357
+ });
1358
+ } else {
1359
+ this.wrapper.css({display: 'block', opacity: 1});
1360
+ }
1361
+ }
1362
+ }.bind(this);
1363
+
1364
+ // Open jBox
1365
+ this.options.delayOpen && !this.isOpen && !this.isClosing && !options.ignoreDelay ? (this.timer = setTimeout(open, this.options.delayOpen)) : open();
1366
+
1367
+ return this;
1368
+ };
1369
+
1370
+ // Close jBox
1371
+ jBox.prototype.close = function(options) {
1372
+ options || (options = {});
1373
+
1374
+ // Abort if jBox was destroyed
1375
+ if (this.isDestroyed) return false;
1376
+
1377
+ // Abort opening
1378
+ this.timer && clearTimeout(this.timer);
1379
+
1380
+ // Block body click for 10ms, so jBox can open on attached elements while closeOnClock = 'body' is true
1381
+ this._blockBodyClick();
1382
+
1383
+ // Block closing
1384
+ if (this.isDisabled) return this;
1385
+
1386
+ // Close function
1387
+ var close = function() {
1388
+
1389
+ // Fire onClose event
1390
+ (this.options.onClose.bind(this))();
1391
+ this.options._onClose && (this.options._onClose.bind(this))();
1392
+
1393
+ // Only close if jBox is open
1394
+ if (this.isOpen) {
1395
+
1396
+ // jBox is not open anymore
1397
+ this.isOpen = false;
1398
+
1399
+ // Detach events
1400
+ this._detachEvents();
1401
+
1402
+ // Unblock scrolling
1403
+ this.options.blockScroll && jQuery('body').removeClass('jBox-blockScroll-' + this.id);
1404
+
1405
+ // Remove overlay
1406
+ this.options.overlay && this._removeOverlay();
1407
+
1408
+ // Only animate if jBox is compleately closed
1409
+ this.options.animation && !this.isOpening && this._animate('close');
1410
+
1411
+ // Fading animation or show immediately
1412
+ if (this.options.fade) {
1413
+ this.wrapper.stop().animate({opacity: 0}, {
1414
+ queue: false,
1415
+ duration: this.options.fade,
1416
+ start: function() {
1417
+ this.isClosing = true;
1418
+ }.bind(this),
1419
+ complete: function() {
1420
+ this.wrapper.css({display: 'none'});
1421
+ this.options.onCloseComplete && (this.options.onCloseComplete.bind(this))();
1422
+ this.options._onCloseComplete && (this.options._onCloseComplete.bind(this))();
1423
+ }.bind(this),
1424
+ always: function() {
1425
+ this.isClosing = false;
1426
+ }.bind(this)
1427
+ });
1428
+ } else {
1429
+ this.wrapper.css({display: 'none', opacity: 0});
1430
+ this.options._onCloseComplete && (this.options._onCloseComplete.bind(this))();
1431
+ }
1432
+ }
1433
+ }.bind(this);
1434
+
1435
+ // Close jBox
1436
+ options.ignoreDelay ? close() : (this.timer = setTimeout(close, Math.max(this.options.delayClose, 10)));
1437
+
1438
+ return this;
1439
+ };
1440
+
1441
+ // Open or close jBox
1442
+ jBox.prototype.toggle = function(options) {
1443
+ this[this.isOpen ? 'close' : 'open'](options);
1444
+ return this;
1445
+ };
1446
+
1447
+ // Block opening and closing
1448
+ jBox.prototype.disable = function() {
1449
+ this.isDisabled = true;
1450
+ return this;
1451
+ };
1452
+
1453
+ // Unblock opening and closing
1454
+ jBox.prototype.enable = function() {
1455
+ this.isDisabled = false;
1456
+ return this;
1457
+ };
1458
+
1459
+ // Get content from ajax
1460
+ jBox.prototype.ajax = function(options) {
1461
+ options || (options = {});
1462
+
1463
+ // Add data from source element if none set in options
1464
+ (this.options.ajax.getData && !options.data && this.source && this.source.attr(this.options.ajax.getData) != undefined) && (options.data = this.source.attr(this.options.ajax.getData) || '');
1465
+
1466
+ // Clone the system options
1467
+ var sysOptions = jQuery.extend(true, {}, this.options.ajax);
1468
+
1469
+ // Abort running ajax call
1470
+ this.ajaxRequest && this.ajaxRequest.abort();
1471
+
1472
+ // Extract events
1473
+ var beforeSend = options.beforeSend || sysOptions.beforeSend || function () {};
1474
+ var complete = options.complete || sysOptions.complete || function () {};
1475
+
1476
+ // Merge options
1477
+ var userOptions = jQuery.extend(true, sysOptions, options);
1478
+
1479
+ // Set new beforeSend event
1480
+ userOptions.beforeSend = function () {
1481
+
1482
+ // Add loading spinner
1483
+ if (userOptions.spinner) {
1484
+ this.wrapper.addClass('jBox-loading');
1485
+ this.spinner = jQuery(userOptions.spinner !== true ? userOptions.spinner : '<div class="jBox-spinner"></div>').appendTo(this.container);
1486
+ }
1487
+
1488
+ (beforeSend.bind(this))();
1489
+ }.bind(this);
1490
+
1491
+ // Set new complete event
1492
+ userOptions.complete = function (response) {
1493
+
1494
+ // Remove spinner
1495
+ this.wrapper.removeClass('jBox-loading');
1496
+ this.spinner && this.spinner.remove();
1497
+
1498
+ // Set new content
1499
+ userOptions.setContent && this.setContent(response.responseText);
1500
+
1501
+ this.ajaxLoaded = true;
1502
+
1503
+ (complete.bind(this))(response);
1504
+ }.bind(this);
1505
+
1506
+ // Send new ajax request
1507
+ this.ajaxRequest = jQuery.ajax(userOptions);
1508
+
1509
+ return this;
1510
+ };
1511
+
1512
+ // Play an audio file
1513
+ jBox.prototype.audio = function(options) {
1514
+ options || (options = {});
1515
+ jBox._audio || (jBox._audio = {});
1516
+
1517
+ // URL required, no IE8 support
1518
+ if (!options.url || this.IE8) return this;
1519
+
1520
+ // Create audio if it doesn't exist
1521
+ if (!jBox._audio[options.url]) {
1522
+ var audio = jQuery('<audio/>');
1523
+ jQuery('<source/>', {src: options.url + '.mp3'}).appendTo(audio);
1524
+ jQuery('<source/>', {src: options.url + '.ogg'}).appendTo(audio);
1525
+ jBox._audio[options.url] = audio[0];
1526
+ }
1527
+
1528
+ // Set volume and play audio
1529
+ jBox._audio[options.url].volume = Math.min((options.volume != undefined ? options.volume : (this.options.volume != undefined ? this.options.volume : 100) / 100), 1);
1530
+ jBox._audio[options.url].pause();
1531
+ try { jBox._audio[options.url].currentTime = 0; } catch (e) {}
1532
+ jBox._audio[options.url].play();
1533
+
1534
+ return this;
1535
+ };
1536
+
1537
+ // Destroy jBox and remove it from DOM
1538
+ // TODO: If no other jBox needs an overlay remove it as well
1539
+ jBox.prototype.destroy = function() {
1540
+ this.detach().close({ignoreDelay: true});
1541
+ this.wrapper && this.wrapper.remove();
1542
+ this.isDestroyed = true;
1543
+ return this;
1544
+ };
1545
+
1546
+ // TODO: Find an option to preload audio files
1547
+
1548
+ // Get a unique ID for jBoxes
1549
+ jBox._getUniqueID = (function () {
1550
+ var i = 1;
1551
+ return function () {
1552
+ return i++;
1553
+ };
1554
+ }());
1555
+
1556
+ // Make jBox usable with jQuery selectors
1557
+ jQuery.fn.jBox = function(type, options) {
1558
+ type || (type = {});
1559
+ options || (options = {});
1560
+ return new jBox(type, jQuery.extend(options, {attach: this}));
1561
+ };
1562
+
1563
+ // Add the .bind() function for IE 8 support
1564
+ if (!Function.prototype.bind) {
1565
+ Function.prototype.bind = function (oThis) {
1566
+ var aArgs = Array.prototype.slice.call(arguments, 1),
1567
+ fToBind = this,
1568
+ fNOP = function () {},
1569
+ fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); };
1570
+ fNOP.prototype = this.prototype;
1571
+ fBound.prototype = new fNOP();
1572
+ return fBound;
1573
+ };
1574
+ }
media/js/lib/jBox.min.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ ---
3
+ description: jBox is a powerful and flexible jQuery plugin, taking care of all your modal windows, tooltips, notices and more.
4
+
5
+ authors: Stephan Wagner (http://stephanwagner.me)
6
+
7
+ license: MIT (http://www.opensource.org/licenses/mit-license.php)
8
+
9
+ requires: jQuery 1.11.0 (http://code.jquery.com/jquery-1.11.0.min.js)
10
+ jQuery 2.1.0 (http://code.jquery.com/jquery-2.1.0.min.js)
11
+
12
+ documentation: http://stephanwagner.me/jBox/documentation
13
+
14
+ demos: http://stephanwagner.me/jBox/demos
15
+ ...
16
+ */
17
+ function jBox(type,options){this.options={id:null,width:"auto",height:"auto",minWidth:null,maxHeight:null,minWidth:null,maxHeight:null,attach:null,trigger:"click",preventDefault:!1,title:null,content:null,getTitle:null,getContent:null,ajax:{url:null,data:"",reload:!1,getData:"data-ajax",setContent:!0,spinner:!0},target:null,position:{x:"center",y:"center"},outside:null,offset:0,attributes:{x:"left",y:"top"},adjustPosition:!1,adjustTracker:!1,adjustDistance:5,fixed:!1,reposition:!1,repositionOnOpen:!0,repositionOnContent:!0,pointer:!1,pointTo:"target",fade:180,animation:null,theme:"Default",addClass:"",overlay:!1,zIndex:1e4,delayOpen:0,delayClose:0,closeOnEsc:!1,closeOnClick:!1,closeOnMouseleave:!1,closeButton:!1,constructOnInit:!1,blockScroll:!1,appendTo:jQuery("body"),draggable:null,dragOver:!0,onInit:function(){},onCreated:function(){},onOpen:function(){},onClose:function(){},onCloseComplete:function(){},confirmButton:"Submit",cancelButton:"Cancel",confirm:null,cancel:null,autoClose:7e3,color:null,stack:!0,audio:!1,volume:100,src:"href",gallery:"data-jbox-image",imageLabel:"title",imageFade:600,imageSize:"contain"},this.defaultOptions={Tooltip:{getContent:"title",trigger:"mouseenter",position:{x:"center",y:"top"},outside:"y",pointer:!0,adjustPosition:!0,reposition:!0},Mouse:{target:"mouse",position:{x:"right",y:"bottom"},offset:15,trigger:"mouseenter",adjustPosition:"flip"},Modal:{target:jQuery(window),fixed:!0,blockScroll:!0,closeOnEsc:!0,closeOnClick:"overlay",closeButton:!0,overlay:!0,animation:"zoomOut"},Confirm:{target:jQuery(window),fixed:!0,attach:jQuery("[data-confirm]"),getContent:"data-confirm",content:"Do you really want to do this?",minWidth:320,maxWidth:460,blockScroll:!0,closeOnEsc:!0,closeOnClick:"overlay",closeButton:!0,overlay:!0,animation:"zoomOut",preventDefault:!0,_onAttach:function(t){if(!this.options.confirm){var i=t.attr("onclick")?t.attr("onclick"):t.attr("href")?t.attr("target")?'window.open("'+t.attr("href")+'", "'+t.attr("target")+'");':'window.location.href = "'+t.attr("href")+'";':"";t.prop("onclick",null).data("jBox-Confirm-submit",i)}},_onCreated:function(){this.footer=jQuery('<div class="jBox-Confirm-footer"/>'),jQuery('<div class="jBox-Confirm-button jBox-Confirm-button-cancel"/>').html(this.options.cancelButton).click(function(){this.options.cancel&&this.options.cancel(),this.close()}.bind(this)).appendTo(this.footer),this.submitButton=jQuery('<div class="jBox-Confirm-button jBox-Confirm-button-submit"/>').html(this.options.confirmButton).appendTo(this.footer),this.footer.appendTo(this.container)},_onOpen:function(){this.submitButton.off("click.jBox-Confirm"+this.id).on("click.jBox-Confirm"+this.id,function(){this.options.confirm?this.options.confirm():eval(this.source.data("jBox-Confirm-submit")),this.close()}.bind(this))}},Notice:{target:jQuery(window),fixed:!0,position:{x:20,y:20},attributes:{x:"right",y:"top"},animation:"zoomIn",closeOnClick:"box",_onInit:function(){this.open(),this.options.delayClose=this.options.autoClose,this.options.delayClose&&this.close()},_onCreated:function(){this.options.color&&this.wrapper.addClass("jBox-Notice-color jBox-Notice-"+this.options.color),this.wrapper.data("jBox-Notice-position",this.options.attributes.x+"-"+this.options.attributes.y)},_onOpen:function(){jQuery.each(jQuery(".jBox-Notice"),function(t,i){return i=jQuery(i),i.attr("id")!=this.id&&i.data("jBox-Notice-position")==this.options.attributes.x+"-"+this.options.attributes.y?this.options.stack?void i.css("margin-"+this.options.attributes.y,parseInt(i.css("margin-"+this.options.attributes.y))+this.wrapper.outerHeight()+10):void i.data("jBox").close({ignoreDelay:!0}):void 0}.bind(this)),this.options.audio&&this.audio({url:this.options.audio,valume:this.options.volume})},_onCloseComplete:function(){this.destroy()}},Image:{target:jQuery(window),fixed:!0,blockScroll:!0,closeOnEsc:!0,closeOnClick:"overlay",closeButton:!0,overlay:!0,animation:"zoomOut",width:800,height:533,attach:jQuery("[data-jbox-image]"),preventDefault:!0,_onInit:function(){this.images=this.currentImage={},this.imageZIndex=1,this.attachedElements&&jQuery.each(this.attachedElements,function(t,i){if(i=jQuery(i),!i.data("jBox-image-gallery")){var s=i.attr(this.options.gallery)||"default";!this.images[s]&&(this.images[s]=[]),this.images[s].push({src:i.attr(this.options.src),label:i.attr(this.options.imageLabel)||""}),"title"==this.options.imageLabel&&i.removeAttr("title"),i.data("jBox-image-gallery",s),i.data("jBox-image-id",this.images[s].length-1)}}.bind(this));var t=function(t,i,s,o){if(!jQuery("#jBox-image-"+t+"-"+i).length){{var e=jQuery("<div/>",{id:"jBox-image-"+t+"-"+i,"class":"jBox-image-container"}).css({backgroundImage:"url("+this.images[t][i].src+")",backgroundSize:this.options.imageSize,opacity:o?1:0,zIndex:s?0:this.imageZIndex++}).appendTo(this.content);jQuery("<div/>",{id:"jBox-image-label-"+t+"-"+i,"class":"jBox-image-label"+(o?" active":"")}).html(this.images[t][i].label).appendTo(this.imageLabel)}!o&&!s&&e.animate({opacity:1},this.options.imageFade)}}.bind(this),i=function(t,i){jQuery(".jBox-image-label.active").removeClass("active"),jQuery("#jBox-image-label-"+t+"-"+i).addClass("active")};this.showImage=function(s){if("open"!=s){var o=this.currentImage.gallery,e=this.currentImage.id+(1*("prev"==s)?-1:1);e=e>this.images[o].length-1?0:0>e?this.images[o].length-1:e}else{var o=this.source.data("jBox-image-gallery"),e=this.source.data("jBox-image-id");jQuery(".jBox-image-pointer-prev, .jBox-image-pointer-next").css({display:this.images[o].length>1?"block":"none"})}if(this.currentImage={gallery:o,id:e},jQuery("#jBox-image-"+o+"-"+e).length)jQuery("#jBox-image-"+o+"-"+e).css({zIndex:this.imageZIndex++,opacity:0}).animate({opacity:1},"open"==s?0:this.options.imageFade),i(o,e);else{this.wrapper.addClass("jBox-loading");{jQuery('<img src="'+this.images[o][e].src+'">').load(function(){t(o,e,!1),i(o,e),this.wrapper.removeClass("jBox-loading")}.bind(this))}}var n=e+1;n=n>this.images[o].length-1?0:0>n?this.images[o].length-1:n,!jQuery("#jBox-image-"+o+"-"+n).length&&jQuery('<img src="'+this.images[o][n].src+'">').load(function(){t(o,n,!0)})}},_onCreated:function(){this.imageLabel=jQuery("<div/>",{id:"jBox-image-label"}).appendTo(this.wrapper),this.wrapper.append(jQuery("<div/>",{"class":"jBox-image-pointer-prev",click:function(){this.showImage("prev")}.bind(this)})).append(jQuery("<div/>",{"class":"jBox-image-pointer-next",click:function(){this.showImage("next")}.bind(this)}))},_onOpen:function(){jQuery("body").addClass("jBox-image-open"),jQuery(document).on("keyup.jBox-"+this.id,function(t){37==t.keyCode&&this.showImage("prev"),39==t.keyCode&&this.showImage("next")}.bind(this)),this.showImage("open")},_onClose:function(){jQuery("body").removeClass("jBox-image-open"),jQuery(document).off("keyup.jBox-"+this.id)},_onCloseComplete:function(){this.wrapper.find(".jBox-image-container").css("opacity",0)}}},"string"==jQuery.type(type)&&(this.type=type,type=this.defaultOptions[type]),this.options=jQuery.extend(!0,this.options,type,options),null===this.options.id&&(this.options.id="jBoxID"+jBox._getUniqueID()),this.id=this.options.id,("center"==this.options.position.x&&"x"==this.options.outside||"center"==this.options.position.y&&"y"==this.options.outside)&&(this.options.outside=!1),(!this.options.outside||"xy"==this.options.outside)&&(this.options.pointer=!1),"object"!=jQuery.type(this.options.offset)&&(this.options.offset={x:this.options.offset,y:this.options.offset}),this.options.offset.x||(this.options.offset.x=0),this.options.offset.y||(this.options.offset.y=0),this.options.adjustDistance="object"!=jQuery.type(this.options.adjustDistance)?{top:this.options.adjustDistance,right:this.options.adjustDistance,bottom:this.options.adjustDistance,left:this.options.adjustDistance}:jQuery.extend({top:5,left:5,right:5,bottom:5},this.options.adjustDistance),this.align=this.options.outside&&"xy"!=this.options.outside?this.options.position[this.options.outside]:"center"!=this.options.position.y&&"number"!=jQuery.type(this.options.position.y)?this.options.position.x:"center"!=this.options.position.x&&"number"!=jQuery.type(this.options.position.x)?this.options.position.y:this.options.attributes.x,this.options.outside&&"xy"!=this.options.outside&&(this.outside=this.options.position[this.options.outside]);var userAgent=navigator.userAgent.toLowerCase();return this.IE8=-1!=userAgent.indexOf("msie")&&8==parseInt(userAgent.split("msie")[1]),this.prefix=-1!=userAgent.indexOf("webkit")?"-webkit-":"",this._getOpp=function(t){return{left:"right",right:"left",top:"bottom",bottom:"top",x:"y",y:"x"}[t]},this._getXY=function(t){return{left:"x",right:"x",top:"y",bottom:"y",center:"x"}[t]},this._getTL=function(t){return{left:"left",right:"left",top:"top",bottom:"top",center:"left",x:"left",y:"top"}[t]},this._supportsSVG=function(){return document.createElement("svg").getAttributeNS},this._createSVG=function(t,i){var s=document.createElementNS("http://www.w3.org/2000/svg",t);return jQuery.each(i,function(t,i){s.setAttribute(i[0],i[1]||"")}),s},this._appendSVG=function(t,i){return i.appendChild(t)},this._create=function(){if(!this.wrapper){if(this.wrapper=jQuery("<div/>",{id:this.id,"class":"jBox-wrapper"+(this.type?" jBox-"+this.type:"")+(this.options.theme?" jBox-"+this.options.theme:"")+(this.options.addClass?" "+this.options.addClass:"")+(this.IE8?" jBox-IE8":"")}).css({position:this.options.fixed?"fixed":"absolute",display:"none",opacity:0,zIndex:this.options.zIndex}).data("jBox",this),this.options.closeOnMouseleave&&this.wrapper.mouseleave(function(t){!this.source||!(t.relatedTarget==this.source[0]||-1!==jQuery.inArray(this.source[0],jQuery(t.relatedTarget).parents("*")))&&this.close()}.bind(this)),"box"==this.options.closeOnClick&&this.wrapper.on("touchend click",function(){this.close({ignoreDelay:!0})}.bind(this)),this.container=jQuery("<div/>",{"class":"jBox-container"}).appendTo(this.wrapper),this.content=jQuery("<div/>",{"class":"jBox-content"}).css({width:this.options.width,height:this.options.height,minWidth:this.options.minWidth,minHeight:this.options.minHeight,maxWidth:this.options.maxWidth,maxHeight:this.options.maxHeight}).appendTo(this.container),this.options.closeButton){if(this.closeButton=jQuery("<div/>",{"class":"jBox-closeButton jBox-noDrag"}).on("touchend click",function(){this.isOpen&&this.close({ignoreDelay:!0})}.bind(this)),this._supportsSVG()){var t=this._createSVG("svg",[["viewBox","0 0 24 24"]]);this._appendSVG(this._createSVG("path",[["d","M22.2,4c0,0,0.5,0.6,0,1.1l-6.8,6.8l6.9,6.9c0.5,0.5,0,1.1,0,1.1L20,22.3c0,0-0.6,0.5-1.1,0L12,15.4l-6.9,6.9c-0.5,0.5-1.1,0-1.1,0L1.7,20c0,0-0.5-0.6,0-1.1L8.6,12L1.7,5.1C1.2,4.6,1.7,4,1.7,4L4,1.7c0,0,0.6-0.5,1.1,0L12,8.5l6.8-6.8c0.5-0.5,1.1,0,1.1,0L22.2,4z"]]),t),this.closeButton.append(t)}else this.wrapper.addClass("jBox-nosvg");("box"==this.options.closeButton||this.options.closeButton===!0&&!this.options.overlay&&!this.options.title)&&(this.wrapper.addClass("jBox-closeButton-box"),this.closeButton.appendTo(this.container))}if(this.wrapper.appendTo(this.options.appendTo),this.options.pointer){if(this.pointer={position:"target"!=this.options.pointTo?this.options.pointTo:this._getOpp(this.outside),xy:this._getXY("target"!=this.options.pointTo?this.options.pointTo:this.outside),align:"center",offset:0},this.pointer.element=jQuery("<div/>",{"class":"jBox-pointer jBox-pointer-"+this.pointer.position}).appendTo(this.wrapper),this.pointer.dimensions={x:this.pointer.element.outerWidth(),y:this.pointer.element.outerHeight()},"string"==jQuery.type(this.options.pointer)){var i=this.options.pointer.split(":");i[0]&&(this.pointer.align=i[0]),i[1]&&(this.pointer.offset=parseInt(i[1]))}this.pointer.alignAttribute="x"==this.pointer.xy?"bottom"==this.pointer.align?"bottom":"top":"right"==this.pointer.align?"right":"left",this.wrapper.css("padding-"+this.pointer.position,this.pointer.dimensions[this.pointer.xy]),this.pointer.element.css(this.pointer.alignAttribute,"center"==this.pointer.align?"50%":0).css("margin-"+this.pointer.alignAttribute,this.pointer.offset),this.pointer.margin={},this.pointer.margin["margin-"+this.pointer.alignAttribute]=this.pointer.offset,"center"==this.pointer.align&&this.pointer.element.css(this.prefix+"transform","translate("+("y"==this.pointer.xy?this.pointer.dimensions.x*-.5+"px":0)+", "+("x"==this.pointer.xy?this.pointer.dimensions.y*-.5+"px":0)+")"),this.pointer.element.css("x"==this.pointer.xy?"width":"height",parseInt(this.pointer.dimensions[this.pointer.xy])+parseInt(this.container.css("border-"+this.pointer.alignAttribute+"-width"))),this.wrapper.addClass("jBox-pointerPosition-"+this.pointer.position)}if(this.setContent(this.options.content,!0),this.setTitle(this.options.title,!0),this.options.draggable){var s="title"==this.options.draggable?this.titleContainer:this.options.draggable.length>0?this.options.draggable:this.options.draggable.selector?jQuery(this.options.draggable.selector):this.wrapper;s.addClass("jBox-draggable").on("mousedown",function(t){if(2!=t.button&&!jQuery(t.target).hasClass("jBox-noDrag")&&!jQuery(t.target).parents(".jBox-noDrag").length){this.options.dragOver&&this.wrapper.css("zIndex")<=jBox.zIndexMax&&(jBox.zIndexMax+=1,this.wrapper.css("zIndex",jBox.zIndexMax));var i=this.wrapper.outerHeight(),s=this.wrapper.outerWidth(),o=this.wrapper.offset().top+i-t.pageY,e=this.wrapper.offset().left+s-t.pageX;jQuery(document).on("mousemove.jBox-draggable-"+this.id,function(t){this.wrapper.offset({top:t.pageY+o-i,left:t.pageX+e-s})}.bind(this)),t.preventDefault()}}.bind(this)).on("mouseup",function(){jQuery(document).off("mousemove.jBox-draggable-"+this.id)}.bind(this)),jBox.zIndexMax=jBox.zIndexMax?Math.max(jBox.zIndexMax,this.options.zIndex):this.options.zIndex}this.options.onCreated.bind(this)(),this.options._onCreated&&this.options._onCreated.bind(this)()}},this.options.constructOnInit&&this._create(),this.options.attach&&this.attach(),this._positionMouse=function(t){this.pos={left:t.pageX,top:t.pageY};var i=function(t,i){return"center"==this.options.position[i]?void(this.pos[t]-=Math.ceil(this.dimensions[i]/2)):(this.pos[t]+=t==this.options.position[i]?-1*this.dimensions[i]-this.options.offset[i]:this.options.offset[i],this.pos[t])}.bind(this);this.wrapper.css({left:i("left","x"),top:i("top","y")}),this.targetDimensions={x:0,y:0,left:t.pageX,top:t.pageY},this._adjustPosition()},this._attachEvents=function(){if(this.options.closeOnEsc&&jQuery(document).on("keyup.jBox-"+this.id,function(t){27==t.keyCode&&this.close({ignoreDelay:!0})}.bind(this)),(this.options.closeOnClick===!0||"body"==this.options.closeOnClick)&&jQuery(document).on("touchend.jBox-"+this.id+" click.jBox-"+this.id,function(t){this.blockBodyClick||"body"==this.options.closeOnClick&&(t.target==this.wrapper[0]||this.wrapper.has(t.target).length)||this.close({ignoreDelay:!0})}.bind(this)),(this.options.adjustPosition&&this.options.adjustTracker||this.options.reposition)&&!this.fixed&&this.outside){var t,i=0,s=150,o=function(){var o=(new Date).getTime();t||(o-i>s&&(this.options.reposition&&this.position(),this.options.adjustTracker&&this._adjustPosition(),i=o),t=setTimeout(function(){t=null,i=(new Date).getTime(),this.options.reposition&&this.position(),this.options.adjustTracker&&this._adjustPosition()}.bind(this),s))}.bind(this);this.options.adjustTracker&&"resize"!=this.options.adjustTracker&&jQuery(window).on("scroll.jBox-"+this.id,function(){o()}.bind(this)),(this.options.adjustTracker&&"scroll"!=this.options.adjustTracker||this.options.reposition)&&jQuery(window).on("resize.jBox-"+this.id,function(){o()}.bind(this))}"mouse"==this.options.target&&jQuery("body").on("mousemove.jBox-"+this.id,function(t){this._positionMouse(t)}.bind(this))},this._detachEvents=function(){this.options.closeOnEsc&&jQuery(document).off("keyup.jBox-"+this.id),(this.options.closeOnClick===!0||"body"==this.options.closeOnClick)&&jQuery(document).off("touchend.jBox-"+this.id+" click.jBox-"+this.id),(this.options.adjustPosition&&this.options.adjustTracker||this.options.reposition)&&(jQuery(window).off("scroll.jBox-"+this.id),jQuery(window).off("resize.jBox-"+this.id)),"mouse"==this.options.target&&jQuery("body").off("mousemove.jBox-"+this.id)},this._addOverlay=function(){this.overlay||(this.overlay=jQuery("#jBox-overlay").length?jQuery("#jBox-overlay").css({zIndex:Math.min(jQuery("#jBox-overlay").css("z-index"),this.options.zIndex-1)}):jQuery("<div/>",{id:"jBox-overlay"}).css({display:"none",opacity:0,zIndex:this.options.zIndex-1}).appendTo(jQuery("body")),("overlay"==this.options.closeButton||this.options.closeButton===!0)&&(jQuery("#jBox-overlay .jBox-closeButton").length>0?jQuery("#jBox-overlay .jBox-closeButton").on("touchend click",function(){this.isOpen&&this.close({ignoreDelay:!0})}.bind(this)):this.overlay.append(this.closeButton)),"overlay"==this.options.closeOnClick&&this.overlay.on("touchend click",function(){this.isOpen&&this.close({ignoreDelay:!0})}.bind(this)));var t=this.overlay.data("jBox")||{};t["jBox-"+this.id]=!0,this.overlay.data("jBox",t),"block"!=this.overlay.css("display")&&(this.options.fade?this.overlay.stop()&&this.overlay.animate({opacity:1},{queue:!1,duration:this.options.fade,start:function(){this.overlay.css({display:"block"})}.bind(this)}):this.overlay.css({display:"block",opacity:1}))},this._removeOverlay=function(){if(this.overlay){var t=this.overlay.data("jBox");delete t["jBox-"+this.id],this.overlay.data("jBox",t),jQuery.isEmptyObject(t)&&(this.options.fade?this.overlay.stop()&&this.overlay.animate({opacity:0},{queue:!1,duration:this.options.fade,complete:function(){this.overlay.css({display:"none"})}.bind(this)}):this.overlay.css({display:"none",opacity:0}))}},this._generateCSS=function(){if(!this.IE8){"object"!=jQuery.type(this.options.animation)&&(this.options.animation={pulse:{open:"pulse",close:"zoomOut"},zoomIn:{open:"zoomIn",close:"zoomIn"},zoomOut:{open:"zoomOut",close:"zoomOut"},move:{open:"move",close:"move"},slide:{open:"slide",close:"slide"},flip:{open:"flip",close:"flip"},tada:{open:"tada",close:"zoomOut"}}[this.options.animation]),this.options.animation.open&&(this.options.animation.open=this.options.animation.open.split(":")),this.options.animation.close&&(this.options.animation.close=this.options.animation.close.split(":")),this.options.animation.openDirection=this.options.animation.open?this.options.animation.open[1]:null,this.options.animation.closeDirection=this.options.animation.close?this.options.animation.close[1]:null,this.options.animation.open&&(this.options.animation.open=this.options.animation.open[0]),this.options.animation.close&&(this.options.animation.close=this.options.animation.close[0]),this.options.animation.open&&(this.options.animation.open+="Open"),this.options.animation.close&&(this.options.animation.close+="Close");var t={pulse:{duration:350,css:[["0%","scale(1)"],["50%","scale(1.1)"],["100%","scale(1)"]]},zoomInOpen:{duration:this.options.fade||180,css:[["0%","scale(0.9)"],["100%","scale(1)"]]},zoomInClose:{duration:this.options.fade||180,css:[["0%","scale(1)"],["100%","scale(0.9)"]]},zoomOutOpen:{duration:this.options.fade||180,css:[["0%","scale(1.1)"],["100%","scale(1)"]]},zoomOutClose:{duration:this.options.fade||180,css:[["0%","scale(1)"],["100%","scale(1.1)"]]},moveOpen:{duration:this.options.fade||180,positions:{top:{"0%":-12},right:{"0%":12},bottom:{"0%":12},left:{"0%":-12}},css:[["0%","translate%XY(%Vpx)"],["100%","translate%XY(0px)"]]},moveClose:{duration:this.options.fade||180,timing:"ease-in",positions:{top:{"100%":-12},right:{"100%":12},bottom:{"100%":12},left:{"100%":-12}},css:[["0%","translate%XY(0px)"],["100%","translate%XY(%Vpx)"]]},slideOpen:{duration:400,positions:{top:{"0%":-400},right:{"0%":400},bottom:{"0%":400},left:{"0%":-400}},css:[["0%","translate%XY(%Vpx)"],["100%","translate%XY(0px)"]]},slideClose:{duration:400,timing:"ease-in",positions:{top:{"100%":-400},right:{"100%":400},bottom:{"100%":400},left:{"100%":-400}},css:[["0%","translate%XY(0px)"],["100%","translate%XY(%Vpx)"]]},flipOpen:{duration:600,css:[["0%","perspective(400px) rotateX(90deg)"],["40%","perspective(400px) rotateX(-15deg)"],["70%","perspective(400px) rotateX(15deg)"],["100%","perspective(400px) rotateX(0deg)"]]},flipClose:{duration:this.options.fade||300,css:[["0%","perspective(400px) rotateX(0deg)"],["100%","perspective(400px) rotateX(90deg)"]]},tada:{duration:800,css:[["0%","scale(1)"],["10%, 20%","scale(0.9) rotate(-3deg)"],["30%, 50%, 70%, 90%","scale(1.1) rotate(3deg)"],["40%, 60%, 80%","scale(1.1) rotate(-3deg)"],["100%","scale(1) rotate(0)"]]}};jQuery.each(["pulse","tada"],function(i,s){t[s+"Open"]=t[s+"Close"]=t[s]});var i=function(i,s){return keyframe_css="@"+this.prefix+"keyframes jBox-animation-"+this.options.animation[i]+"-"+i+(s?"-"+s:"")+" {",jQuery.each(t[this.options.animation[i]].css,function(o,e){var n=s?e[1].replace("%XY",this._getXY(s).toUpperCase()):e[1];t[this.options.animation[i]].positions&&(n=n.replace("%V",t[this.options.animation[i]].positions[s][e[0]])),keyframe_css+=e[0]+" {"+this.prefix+"transform:"+n+";}"}.bind(this)),keyframe_css+="}",keyframe_css+=".jBox-animation-"+this.options.animation[i]+"-"+i+(s?"-"+s:"")+" {",keyframe_css+=this.prefix+"animation-duration: "+t[this.options.animation[i]].duration+"ms;",keyframe_css+=this.prefix+"animation-name: jBox-animation-"+this.options.animation[i]+"-"+i+(s?"-"+s:"")+";",keyframe_css+=t[this.options.animation[i]].timing?this.prefix+"animation-timing-function: "+t[this.options.animation[i]].timing+";":"",keyframe_css+="}"}.bind(this),s="";jQuery.each(["open","close"],function(o,e){return this.options.animation[e]&&t[this.options.animation[e]]&&("close"!=e||this.options.fade)?void(t[this.options.animation[e]].positions?jQuery.each(["top","right","bottom","left"],function(t,o){s+=i(e,o)}):s+=i(e)):""}.bind(this)),jQuery("<style/>").append(s).appendTo(jQuery("head"))}},this._blockBodyClick=function(){this.blockBodyClick=!0,setTimeout(function(){this.blockBodyClick=!1}.bind(this),10)},this.options.animation&&this._generateCSS(),this._animate=function(t){if(!this.IE8){if(t||(t=this.isOpen?"open":"close"),!this.options.fade&&"close"==t)return null;var i=this.options.animation[t+"Direction"]||("center"!=this.align?this.align:this.options.attributes.x);this.flipped&&this._getXY(i)==this._getXY(this.align)&&(i=this._getOpp(i));var s="jBox-animation-"+this.options.animation[t]+"-"+t+" jBox-animation-"+this.options.animation[t]+"-"+t+"-"+i;this.wrapper.addClass(s);var o=1e3*parseFloat(this.wrapper.css(this.prefix+"animation-duration"));"close"==t&&(o=Math.min(o,this.options.fade)),setTimeout(function(){this.wrapper.removeClass(s)}.bind(this),o)}},this._abortAnimation=function(){if(!this.IE8){var t="jBox-animation",i=this.wrapper.attr("class").split(" ").filter(function(i){return 0!==i.lastIndexOf(t,0)});this.wrapper.attr("class",i.join(" "))}},this._adjustPosition=function(){if(!this.options.adjustPosition)return null;this.positionAdjusted&&(this.wrapper.css(this.pos),this.pointer&&this.wrapper.css("padding",0).css("padding-"+this._getOpp(this.outside),this.pointer.dimensions[this._getXY(this.outside)]).removeClass("jBox-pointerPosition-"+this._getOpp(this.pointer.position)).addClass("jBox-pointerPosition-"+this.pointer.position),this.pointer&&this.pointer.element.attr("class","jBox-pointer jBox-pointer-"+this._getOpp(this.outside)).css(this.pointer.margin),this.positionAdjusted=!1,this.flipped=!1);var t=jQuery(window),i={x:t.width(),y:t.height(),top:this.options.fixed&&this.target.data("jBox-fixed")?0:t.scrollTop(),left:this.options.fixed&&this.target.data("jBox-fixed")?0:t.scrollLeft()};i.bottom=i.top+i.y,i.right=i.left+i.x;var s=i.top>this.pos.top-(this.options.adjustDistance.top||0),o=i.right<this.pos.left+this.dimensions.x+(this.options.adjustDistance.right||0),e=i.bottom<this.pos.top+this.dimensions.y+(this.options.adjustDistance.bottom||0),n=i.left>this.pos.left-(this.options.adjustDistance.left||0),a=n?"left":o?"right":null,h=s?"top":e?"bottom":null,p=a||h;if(p){"move"==this.options.adjustPosition||a!=this.outside&&h!=this.outside||("mouse"==this.target&&(this.outside="right"),("top"==this.outside||"left"==this.outside?i[this._getXY(this.outside)]-(this.targetDimensions[this._getTL(this.outside)]-i[this._getTL(this.outside)])-this.targetDimensions[this._getXY(this.outside)]+this.options.offset[this._getXY(this.outside)]:this.targetDimensions[this._getTL(this.outside)]-i[this._getTL(this.outside)]-this.options.offset[this._getXY(this.outside)])>this.dimensions[this._getXY(this.outside)]+parseInt(this.options.adjustDistance[this._getOpp(this.outside)])&&(this.wrapper.css(this._getTL(this.outside),this.pos[this._getTL(this.outside)]+(this.dimensions[this._getXY(this.outside)]+this.options.offset[this._getXY(this.outside)]*("top"==this.outside||"left"==this.outside?-2:2)+this.targetDimensions[this._getXY(this.outside)])*("top"==this.outside||"left"==this.outside?1:-1)),this.pointer&&this.wrapper.removeClass("jBox-pointerPosition-"+this.pointer.position).addClass("jBox-pointerPosition-"+this._getOpp(this.pointer.position)).css("padding",0).css("padding-"+this.outside,this.pointer.dimensions[this._getXY(this.outside)]),this.pointer&&this.pointer.element.attr("class","jBox-pointer jBox-pointer-"+this.outside),this.positionAdjusted=!0,this.flipped=!0));var r="x"==this._getXY(this.outside)?h:a;if(this.pointer&&"flip"!=this.options.adjustPosition&&this._getXY(r)==this._getOpp(this._getXY(this.outside))){if("center"==this.pointer.align)var l=this.dimensions[this._getXY(r)]/2-this.pointer.dimensions[this._getOpp(this.pointer.xy)]/2-parseInt(this.pointer.element.css("margin-"+this.pointer.alignAttribute))*(r!=this._getTL(r)?-1:1);else var l=r==this.pointer.alignAttribute?parseInt(this.pointer.element.css("margin-"+this.pointer.alignAttribute)):this.dimensions[this._getXY(r)]-parseInt(this.pointer.element.css("margin-"+this.pointer.alignAttribute))-this.pointer.dimensions[this._getXY(r)];spaceDiff=r==this._getTL(r)?i[this._getTL(r)]-this.pos[this._getTL(r)]+this.options.adjustDistance[r]:-1*(i[this._getOpp(this._getTL(r))]-this.pos[this._getTL(r)]-this.options.adjustDistance[r]-this.dimensions[this._getXY(r)]),r==this._getOpp(this._getTL(r))&&this.pos[this._getTL(r)]-spaceDiff<i[this._getTL(r)]+this.options.adjustDistance[this._getTL(r)]&&(spaceDiff-=i[this._getTL(r)]+this.options.adjustDistance[this._getTL(r)]-(this.pos[this._getTL(r)]-spaceDiff)),spaceDiff=Math.min(spaceDiff,l),l>=spaceDiff&&spaceDiff>0&&(this.pointer.element.css("margin-"+this.pointer.alignAttribute,parseInt(this.pointer.element.css("margin-"+this.pointer.alignAttribute))-spaceDiff*(r!=this.pointer.alignAttribute?-1:1)),this.wrapper.css(this._getTL(r),this.pos[this._getTL(r)]+spaceDiff*(r!=this._getTL(r)?-1:1)),this.positionAdjusted=!0)}}},this.options.onInit.bind(this)(),this.options._onInit&&this.options._onInit.bind(this)(),this}jBox.prototype.attach=function(t,i){return t||(t=jQuery(this.options.attach.selector||this.options.attach)),i||(i=this.options.trigger),t&&t.length&&jQuery.each(t,function(t,s){s=jQuery(s),s.data("jBox-attached-"+this.id)||("title"==this.options.getContent&&void 0!=s.attr("title")&&s.data("jBox-getContent",s.attr("title")).removeAttr("title"),this.attachedElements||(this.attachedElements=[]),this.attachedElements.push(s[0]),s.on(i+".jBox-attach-"+this.id,function(t){if(this.timer&&clearTimeout(this.timer),"mouseenter"!=i||!this.isOpen||this.source[0]!=s[0]){if(this.isOpen&&this.source&&this.source[0]!=s[0])var o=!0;this.source=s,!this.options.target&&(this.target=s),"click"==i&&this.options.preventDefault&&t.preventDefault(),this["click"!=i||o?"open":"toggle"]()}}.bind(this)),"mouseenter"==this.options.trigger&&s.on("mouseleave",function(t){(!this.options.closeOnMouseleave||t.relatedTarget!=this.wrapper[0]&&!jQuery(t.relatedTarget).parents("#"+this.id).length)&&this.close()}.bind(this)),s.data("jBox-attached-"+this.id,i),this.options._onAttach&&this.options._onAttach.bind(this)(s))}.bind(this)),this},jBox.prototype.detach=function(t){return t||(t=this.attachedElements||[]),t&&t.length&&jQuery.each(t,function(t,i){i=jQuery(i),i.data("jBox-attached-"+this.id)&&(i.off(i.data("jBox-attached-"+this.id)+".jBox-attach-"+this.id),i.data("jBox-attached-"+this.id,null)),this.attachedElements=jQuery.grep(this.attachedElements,function(t){return t!=i[0]})}.bind(this)),this},jBox.prototype.setTitle=function(t,i){var s=this.wrapper.height(),o=this.wrapper.width();return null==t||void 0==t?this:(!this.wrapper&&this._create(),this.title||(this.titleContainer=jQuery("<div/>",{"class":"jBox-title"}),this.title=jQuery("<div/>").appendTo(this.titleContainer),this.wrapper.addClass("jBox-hasTitle"),("title"==this.options.closeButton||this.options.closeButton===!0&&!this.options.overlay)&&(this.wrapper.addClass("jBox-closeButton-title"),this.closeButton.appendTo(this.titleContainer)),this.titleContainer.insertBefore(this.content)),this.title.html(t),!i&&this.options.repositionOnContent&&(s!=this.wrapper.height()||o!=this.wrapper.width())&&this.position(),this)},jBox.prototype.setContent=function(t,i){if(null==t)return this;!this.wrapper&&this._create();var s=this.wrapper.height(),o=this.wrapper.width(),e=jQuery("body").height(),n=jQuery("body").width();switch(this.content.children("[data-jbox-content-appended]").appendTo("body").css({display:"none"}),jQuery.type(t)){case"string":this.content.html(t);break;case"object":this.content.html(""),t.attr("data-jbox-content-appended",1).appendTo(this.content).css({display:"block"})}var a={x:n-jQuery("body").width(),y:e-jQuery("body").height()};return!i&&this.options.repositionOnContent&&(s!=this.wrapper.height()||o!=this.wrapper.width())&&this.position({adjustOffset:a}),this},jBox.prototype.setDimensions=function(t,i,s){!this.wrapper&&this._create(),this.content.css(t,i),(void 0==s||s)&&this.position()},jBox.prototype.setWidth=function(t,i){this.setDimensions("width",t,i)},jBox.prototype.setHeight=function(t,i){this.setDimensions("height",t,i)},jBox.prototype.position=function(t){if(t||(t={}),this.target=t.target||this.target||this.options.target||jQuery(window),this.dimensions={x:this.wrapper.outerWidth(),y:this.wrapper.outerHeight()},"mouse"!=this.target){if("center"==this.options.position.x&&"center"==this.options.position.y)return this.wrapper.css({left:"50%",top:"50%",marginLeft:this.dimensions.x*-.5+this.options.offset.x,marginTop:this.dimensions.y*-.5+this.options.offset.y}),this;var i=this.target.offset();!this.target.data("jBox-fixed")&&this.target.data("jBox-fixed",this.target[0]==jQuery(window)[0]||"fixed"!=this.target.css("position")&&this.target.parents().filter(function(){return"fixed"==jQuery(this).css("position")}).length<=0?"static":"fixed"),"fixed"==this.target.data("jBox-fixed")&&this.options.fixed&&(i.top=i.top-jQuery(window).scrollTop(),i.left=i.left-jQuery(window).scrollLeft()),this.targetDimensions={x:this.target.outerWidth(),y:this.target.outerHeight(),top:i?i.top:0,left:i?i.left:0},this.pos={};var s=function(t){if(-1==jQuery.inArray(this.options.position[t],["top","right","bottom","left","center"]))return void(this.pos[this.options.attributes[t]]=this.options.position[t]);var i=this.options.attributes[t]="x"==t?"left":"top";return this.pos[i]=this.targetDimensions[i],"center"==this.options.position[t]?void(this.pos[i]+=Math.ceil((this.targetDimensions[t]-this.dimensions[t])/2)):(i!=this.options.position[t]&&(this.pos[i]+=this.targetDimensions[t]-this.dimensions[t]),void((this.options.outside==t||"xy"==this.options.outside)&&(this.pos[i]+=this.dimensions[t]*(i!=this.options.position[t]?1:-1))))}.bind(this);if(s("x"),s("y"),this.options.pointer&&"number"!=jQuery.type(this.options.position.x)&&"number"!=jQuery.type(this.options.position.y)){var o=0;switch(this.pointer.align){case"center":"center"!=this.options.position[this._getOpp(this.options.outside)]&&(o+=this.dimensions[this._getOpp(this.options.outside)]/2);break;default:switch(this.options.position[this._getOpp(this.options.outside)]){case"center":o+=(this.dimensions[this._getOpp(this.options.outside)]/2-this.pointer.dimensions[this._getOpp(this.options.outside)]/2)*(this.pointer.align==this._getTL(this.pointer.align)?1:-1);
18
+ break;default:o+=this.pointer.align!=this.options.position[this._getOpp(this.options.outside)]?this.dimensions[this._getOpp(this.options.outside)]*(-1!==jQuery.inArray(this.pointer.align,["top","left"])?1:-1)+this.pointer.dimensions[this._getOpp(this.options.outside)]/2*(-1!==jQuery.inArray(this.pointer.align,["top","left"])?-1:1):this.pointer.dimensions[this._getOpp(this.options.outside)]/2*(-1!==jQuery.inArray(this.pointer.align,["top","left"])?1:-1)}}o*=this.options.position[this._getOpp(this.options.outside)]==this.pointer.alignAttribute?-1:1,o+=this.pointer.offset*(this.pointer.align==this._getOpp(this._getTL(this.pointer.align))?1:-1),this.pos[this._getTL(this._getOpp(this.pointer.xy))]+=o}return t.adjustOffset&&t.adjustOffset.x&&(this.pos[this.options.attributes.x]+=parseInt(t.adjustOffset.x)*("left"==this.options.attributes.x?1:-1)),t.adjustOffset&&t.adjustOffset.y&&(this.pos[this.options.attributes.y]+=parseInt(t.adjustOffset.y)*("top"==this.options.attributes.y?1:-1)),this.pos[this.options.attributes.x]+=this.options.offset.x,this.pos[this.options.attributes.y]+=this.options.offset.y,this.wrapper.css(this.pos),this._adjustPosition(),this}},jBox.prototype.open=function(t){if(t||(t={}),this.isDestroyed)return!1;if(!this.wrapper&&this._create(),this.timer&&clearTimeout(this.timer),this._blockBodyClick(),this.isDisabled)return this;var i=function(){this.source&&this.options.getTitle&&(this.source.attr(this.options.getTitle)&&this.setTitle(this.source.attr(this.options.getTitle)),!0),this.source&&this.options.getContent&&(this.source.data("jBox-getContent")?this.setContent(this.source.data("jBox-getContent"),!0):this.source.attr(this.options.getContent)?this.setContent(this.source.attr(this.options.getContent),!0):null),this.options.onOpen.bind(this)(),this.options._onOpen&&this.options._onOpen.bind(this)(),(this.options.ajax&&this.options.ajax.url&&(!this.ajaxLoaded||this.options.ajax.reload)||t.ajax&&t.ajax.url)&&this.ajax(t.ajax||null),(!this.positionedOnOpen||this.options.repositionOnOpen)&&this.position({target:t.target})&&(this.positionedOnOpen=!0),this.isClosing&&this._abortAnimation(),this.isOpen||(this.isOpen=!0,this._attachEvents(),this.options.blockScroll&&jQuery("body").addClass("jBox-blockScroll-"+this.id),this.options.overlay&&this._addOverlay(),this.options.animation&&!this.isClosing&&this._animate("open"),this.options.fade?this.wrapper.stop().animate({opacity:1},{queue:!1,duration:this.options.fade,start:function(){this.isOpening=!0,this.wrapper.css({display:"block"})}.bind(this),always:function(){this.isOpening=!1}.bind(this)}):this.wrapper.css({display:"block",opacity:1}))}.bind(this);return!this.options.delayOpen||this.isOpen||this.isClosing||t.ignoreDelay?i():this.timer=setTimeout(i,this.options.delayOpen),this},jBox.prototype.close=function(t){if(t||(t={}),this.isDestroyed)return!1;if(this.timer&&clearTimeout(this.timer),this._blockBodyClick(),this.isDisabled)return this;var i=function(){this.options.onClose.bind(this)(),this.options._onClose&&this.options._onClose.bind(this)(),this.isOpen&&(this.isOpen=!1,this._detachEvents(),this.options.blockScroll&&jQuery("body").removeClass("jBox-blockScroll-"+this.id),this.options.overlay&&this._removeOverlay(),this.options.animation&&!this.isOpening&&this._animate("close"),this.options.fade?this.wrapper.stop().animate({opacity:0},{queue:!1,duration:this.options.fade,start:function(){this.isClosing=!0}.bind(this),complete:function(){this.wrapper.css({display:"none"}),this.options.onCloseComplete&&this.options.onCloseComplete.bind(this)(),this.options._onCloseComplete&&this.options._onCloseComplete.bind(this)()}.bind(this),always:function(){this.isClosing=!1}.bind(this)}):(this.wrapper.css({display:"none",opacity:0}),this.options._onCloseComplete&&this.options._onCloseComplete.bind(this)()))}.bind(this);return t.ignoreDelay?i():this.timer=setTimeout(i,Math.max(this.options.delayClose,10)),this},jBox.prototype.toggle=function(t){return this[this.isOpen?"close":"open"](t),this},jBox.prototype.disable=function(){return this.isDisabled=!0,this},jBox.prototype.enable=function(){return this.isDisabled=!1,this},jBox.prototype.ajax=function(t){t||(t={}),this.options.ajax.getData&&!t.data&&this.source&&void 0!=this.source.attr(this.options.ajax.getData)&&(t.data=this.source.attr(this.options.ajax.getData)||"");var i=jQuery.extend(!0,{},this.options.ajax);this.ajaxRequest&&this.ajaxRequest.abort();var s=t.beforeSend||i.beforeSend||function(){},o=t.complete||i.complete||function(){},e=jQuery.extend(!0,i,t);return e.beforeSend=function(){e.spinner&&(this.wrapper.addClass("jBox-loading"),this.spinner=jQuery(e.spinner!==!0?e.spinner:'<div class="jBox-spinner"></div>').appendTo(this.container)),s.bind(this)()}.bind(this),e.complete=function(t){this.wrapper.removeClass("jBox-loading"),this.spinner&&this.spinner.remove(),e.setContent&&this.setContent(t.responseText),this.ajaxLoaded=!0,o.bind(this)(t)}.bind(this),this.ajaxRequest=jQuery.ajax(e),this},jBox.prototype.audio=function(t){if(t||(t={}),jBox._audio||(jBox._audio={}),!t.url||this.IE8)return this;if(!jBox._audio[t.url]){var i=jQuery("<audio/>");jQuery("<source/>",{src:t.url+".mp3"}).appendTo(i),jQuery("<source/>",{src:t.url+".ogg"}).appendTo(i),jBox._audio[t.url]=i[0]}jBox._audio[t.url].volume=Math.min(void 0!=t.volume?t.volume:(void 0!=this.options.volume?this.options.volume:100)/100,1),jBox._audio[t.url].pause();try{jBox._audio[t.url].currentTime=0}catch(s){}return jBox._audio[t.url].play(),this},jBox.prototype.destroy=function(){return this.detach().close({ignoreDelay:!0}),this.wrapper&&this.wrapper.remove(),this.isDestroyed=!0,this},jBox._getUniqueID=function(){var t=1;return function(){return t++}}(),jQuery.fn.jBox=function(t,i){return t||(t={}),i||(i={}),new jBox(t,jQuery.extend(i,{attach:this}))},Function.prototype.bind||(Function.prototype.bind=function(t){var i=Array.prototype.slice.call(arguments,1),s=this,o=function(){},e=function(){return s.apply(this instanceof o&&t?this:t,i.concat(Array.prototype.slice.call(arguments)))};return o.prototype=this.prototype,e.prototype=new o,e});
media/js/lib/jquery.ui.timepicker.js ADDED
@@ -0,0 +1,1496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI Timepicker
3
+ *
4
+ * Copyright 2010-2013, Francois Gelinas
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://fgelinas.com/code/timepicker
9
+ *
10
+ * Depends:
11
+ * jquery.ui.core.js
12
+ * jquery.ui.position.js (only if position settings are used)
13
+ *
14
+ * Change version 0.1.0 - moved the t-rex up here
15
+ *
16
+ ____
17
+ ___ .-~. /_"-._
18
+ `-._~-. / /_ "~o\ :Y
19
+ \ \ / : \~x. ` ')
20
+ ] Y / | Y< ~-.__j
21
+ / ! _.--~T : l l< /.-~
22
+ / / ____.--~ . ` l /~\ \<|Y
23
+ / / .-~~" /| . ',-~\ \L|
24
+ / / / .^ \ Y~Y \.^>/l_ "--'
25
+ / Y .-"( . l__ j_j l_/ /~_.-~ .
26
+ Y l / \ ) ~~~." / `/"~ / \.__/l_
27
+ | \ _.-" ~-{__ l : l._Z~-.___.--~
28
+ | ~---~ / ~~"---\_ ' __[>
29
+ l . _.^ ___ _>-y~
30
+ \ \ . .-~ .-~ ~>--" /
31
+ \ ~---" / ./ _.-'
32
+ "-.,_____.,_ _.--~\ _.-~
33
+ ~~ ( _} -Row
34
+ `. ~(
35
+ ) \
36
+ /,`--'~\--'~\
37
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38
+ ->T-Rex<-
39
+ */
40
+
41
+ (function ($) {
42
+
43
+ $.extend($.ui, { timepicker: { version: "0.3.3"} });
44
+
45
+ var PROP_NAME = 'timepicker',
46
+ tpuuid = new Date().getTime();
47
+
48
+ /* Time picker manager.
49
+ Use the singleton instance of this class, $.timepicker, to interact with the time picker.
50
+ Settings for (groups of) time pickers are maintained in an instance object,
51
+ allowing multiple different settings on the same page. */
52
+
53
+ function Timepicker() {
54
+ this.debug = true; // Change this to true to start debugging
55
+ this._curInst = null; // The current instance in use
56
+ this._disabledInputs = []; // List of time picker inputs that have been disabled
57
+ this._timepickerShowing = false; // True if the popup picker is showing , false if not
58
+ this._inDialog = false; // True if showing within a "dialog", false if not
59
+ this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
60
+ this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
61
+ this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
62
+ this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
63
+ this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
64
+
65
+ this.regional = []; // Available regional settings, indexed by language code
66
+ this.regional[''] = { // Default regional settings
67
+ hourText: 'Hour', // Display text for hours section
68
+ minuteText: 'Minute', // Display text for minutes link
69
+ amPmText: ['AM', 'PM'], // Display text for AM PM
70
+ closeButtonText: 'Done', // Text for the confirmation button (ok button)
71
+ nowButtonText: 'Now', // Text for the now button
72
+ deselectButtonText: 'Deselect' // Text for the deselect button
73
+ };
74
+ this._defaults = { // Global defaults for all the time picker instances
75
+ showOn: 'focus', // 'focus' for popup on focus,
76
+ // 'button' for trigger button, or 'both' for either (not yet implemented)
77
+ button: null, // 'button' element that will trigger the timepicker
78
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
79
+ showOptions: {}, // Options for enhanced animations
80
+ appendText: '', // Display text following the input box, e.g. showing the format
81
+
82
+ beforeShow: null, // Define a callback function executed before the timepicker is shown
83
+ onSelect: null, // Define a callback function when a hour / minutes is selected
84
+ onClose: null, // Define a callback function when the timepicker is closed
85
+
86
+ timeSeparator: ':', // The character to use to separate hours and minutes.
87
+ periodSeparator: ' ', // The character to use to separate the time from the time period.
88
+ showPeriod: false, // Define whether or not to show AM/PM with selected time
89
+ showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
90
+ showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
91
+ showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
92
+ altField: '', // Selector for an alternate field to store selected time into
93
+ defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
94
+ // (set to 'now' for the current time, '' for no highlighted time)
95
+ myPosition: 'left top', // Position of the dialog relative to the input.
96
+ // see the position utility for more info : http://jqueryui.com/demos/position/
97
+ atPosition: 'left bottom', // Position of the input element to match
98
+ // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
99
+ //NEW: 2011-02-03
100
+ onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
101
+ onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
102
+
103
+ hours: {
104
+ starts: 0, // first displayed hour
105
+ ends: 23 // last displayed hour
106
+ },
107
+ minutes: {
108
+ starts: 0, // first displayed minute
109
+ ends: 55, // last displayed minute
110
+ interval: 5, // interval of displayed minutes
111
+ manual: [] // optional extra manual entries for minutes
112
+ },
113
+ rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
114
+ // 2011-08-05 0.2.4
115
+ showHours: true, // display the hours section of the dialog
116
+ showMinutes: true, // display the minute section of the dialog
117
+ optionalMinutes: false, // optionally parse inputs of whole hours with minutes omitted
118
+
119
+ // buttons
120
+ showCloseButton: false, // shows an OK button to confirm the edit
121
+ showNowButton: false, // Shows the 'now' button
122
+ showDeselectButton: false, // Shows the deselect time button
123
+
124
+ maxTime: {
125
+ hour: null,
126
+ minute: null
127
+ },
128
+ minTime: {
129
+ hour: null,
130
+ minute: null
131
+ }
132
+
133
+ };
134
+ $.extend(this._defaults, this.regional['']);
135
+
136
+ this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
137
+ }
138
+
139
+ $.extend(Timepicker.prototype, {
140
+ /* Class name added to elements to indicate already configured with a time picker. */
141
+ markerClassName: 'hasTimepicker',
142
+
143
+ /* Debug logging (if enabled). */
144
+ log: function () {
145
+ if (this.debug)
146
+ console.log.apply('', arguments);
147
+ },
148
+
149
+ _widgetTimepicker: function () {
150
+ return this.tpDiv;
151
+ },
152
+
153
+ /* Override the default settings for all instances of the time picker.
154
+ @param settings object - the new settings to use as defaults (anonymous object)
155
+ @return the manager object */
156
+ setDefaults: function (settings) {
157
+ extendRemove(this._defaults, settings || {});
158
+ return this;
159
+ },
160
+
161
+ /* Attach the time picker to a jQuery selection.
162
+ @param target element - the target input field or division or span
163
+ @param settings object - the new settings to use for this time picker instance (anonymous) */
164
+ _attachTimepicker: function (target, settings) {
165
+ // check for settings on the control itself - in namespace 'time:'
166
+ var inlineSettings = null;
167
+ for (var attrName in this._defaults) {
168
+ var attrValue = target.getAttribute('time:' + attrName);
169
+ if (attrValue) {
170
+ inlineSettings = inlineSettings || {};
171
+ try {
172
+ inlineSettings[attrName] = eval(attrValue);
173
+ } catch (err) {
174
+ inlineSettings[attrName] = attrValue;
175
+ }
176
+ }
177
+ }
178
+ var nodeName = target.nodeName.toLowerCase();
179
+ var inline = (nodeName == 'div' || nodeName == 'span');
180
+
181
+ if (!target.id) {
182
+ this.uuid += 1;
183
+ target.id = 'tp' + this.uuid;
184
+ }
185
+ var inst = this._newInst($(target), inline);
186
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
187
+ if (nodeName == 'input') {
188
+ this._connectTimepicker(target, inst);
189
+ // init inst.hours and inst.minutes from the input value
190
+ this._setTimeFromField(inst);
191
+ } else if (inline) {
192
+ this._inlineTimepicker(target, inst);
193
+ }
194
+
195
+
196
+ },
197
+
198
+ /* Create a new instance object. */
199
+ _newInst: function (target, inline) {
200
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
201
+ return {
202
+ id: id, input: target, // associated target
203
+ inline: inline, // is timepicker inline or not :
204
+ tpDiv: (!inline ? this.tpDiv : // presentation div
205
+ $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget ui-helper-clearfix"></div>'))
206
+ };
207
+ },
208
+
209
+ /* Attach the time picker to an input field. */
210
+ _connectTimepicker: function (target, inst) {
211
+ var input = $(target);
212
+ inst.append = $([]);
213
+ inst.trigger = $([]);
214
+ if (input.hasClass(this.markerClassName)) { return; }
215
+ this._attachments(input, inst);
216
+ input.addClass(this.markerClassName).
217
+ keydown(this._doKeyDown).
218
+ keyup(this._doKeyUp).
219
+ bind("setData.timepicker", function (event, key, value) {
220
+ inst.settings[key] = value;
221
+ }).
222
+ bind("getData.timepicker", function (event, key) {
223
+ return this._get(inst, key);
224
+ });
225
+ $.data(target, PROP_NAME, inst);
226
+ },
227
+
228
+ /* Handle keystrokes. */
229
+ _doKeyDown: function (event) {
230
+ var inst = $.timepicker._getInst(event.target);
231
+ var handled = true;
232
+ inst._keyEvent = true;
233
+ if ($.timepicker._timepickerShowing) {
234
+ switch (event.keyCode) {
235
+ case 9: $.timepicker._hideTimepicker();
236
+ handled = false;
237
+ break; // hide on tab out
238
+ case 13:
239
+ $.timepicker._updateSelectedValue(inst);
240
+ $.timepicker._hideTimepicker();
241
+
242
+ return false; // don't submit the form
243
+ break; // select the value on enter
244
+ case 27: $.timepicker._hideTimepicker();
245
+ break; // hide on escape
246
+ default: handled = false;
247
+ }
248
+ }
249
+ else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
250
+ $.timepicker._showTimepicker(this);
251
+ }
252
+ else {
253
+ handled = false;
254
+ }
255
+ if (handled) {
256
+ event.preventDefault();
257
+ event.stopPropagation();
258
+ }
259
+ },
260
+
261
+ /* Update selected time on keyUp */
262
+ /* Added verion 0.0.5 */
263
+ _doKeyUp: function (event) {
264
+ var inst = $.timepicker._getInst(event.target);
265
+ $.timepicker._setTimeFromField(inst);
266
+ $.timepicker._updateTimepicker(inst);
267
+ },
268
+
269
+ /* Make attachments based on settings. */
270
+ _attachments: function (input, inst) {
271
+ var appendText = this._get(inst, 'appendText');
272
+ var isRTL = this._get(inst, 'isRTL');
273
+ if (inst.append) { inst.append.remove(); }
274
+ if (appendText) {
275
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
276
+ input[isRTL ? 'before' : 'after'](inst.append);
277
+ }
278
+ input.unbind('focus.timepicker', this._showTimepicker);
279
+ input.unbind('click.timepicker', this._adjustZIndex);
280
+
281
+ if (inst.trigger) { inst.trigger.remove(); }
282
+
283
+ var showOn = this._get(inst, 'showOn');
284
+ if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
285
+ input.bind("focus.timepicker", this._showTimepicker);
286
+ input.bind("click.timepicker", this._adjustZIndex);
287
+ }
288
+ if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
289
+ var button = this._get(inst, 'button');
290
+
291
+ // Add button if button element is not set
292
+ if(button == null) {
293
+ button = $('<button class="ui-timepicker-trigger" type="button">...</button>');
294
+ input.after(button);
295
+ }
296
+
297
+ $(button).bind("click.timepicker", function () {
298
+ if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
299
+ $.timepicker._hideTimepicker();
300
+ } else if (!inst.input.is(':disabled')) {
301
+ $.timepicker._showTimepicker(input[0]);
302
+ }
303
+ return false;
304
+ });
305
+
306
+ }
307
+ },
308
+
309
+
310
+ /* Attach an inline time picker to a div. */
311
+ _inlineTimepicker: function(target, inst) {
312
+ var divSpan = $(target);
313
+ if (divSpan.hasClass(this.markerClassName))
314
+ return;
315
+ divSpan.addClass(this.markerClassName).append(inst.tpDiv).
316
+ bind("setData.timepicker", function(event, key, value){
317
+ inst.settings[key] = value;
318
+ }).bind("getData.timepicker", function(event, key){
319
+ return this._get(inst, key);
320
+ });
321
+ $.data(target, PROP_NAME, inst);
322
+
323
+ this._setTimeFromField(inst);
324
+ this._updateTimepicker(inst);
325
+ inst.tpDiv.show();
326
+ },
327
+
328
+ _adjustZIndex: function(input) {
329
+ input = input.target || input;
330
+ var inst = $.timepicker._getInst(input);
331
+ inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
332
+ },
333
+
334
+ /* Pop-up the time picker for a given input field.
335
+ @param input element - the input field attached to the time picker or
336
+ event - if triggered by focus */
337
+ _showTimepicker: function (input) {
338
+ input = input.target || input;
339
+ if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
340
+
341
+ if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
342
+
343
+ // fix v 0.0.8 - close current timepicker before showing another one
344
+ $.timepicker._hideTimepicker();
345
+
346
+ var inst = $.timepicker._getInst(input);
347
+ if ($.timepicker._curInst && $.timepicker._curInst != inst) {
348
+ $.timepicker._curInst.tpDiv.stop(true, true);
349
+ }
350
+ var beforeShow = $.timepicker._get(inst, 'beforeShow');
351
+ extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
352
+ inst.lastVal = null;
353
+ $.timepicker._lastInput = input;
354
+
355
+ $.timepicker._setTimeFromField(inst);
356
+
357
+ // calculate default position
358
+ if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
359
+ if (!$.timepicker._pos) { // position below input
360
+ $.timepicker._pos = $.timepicker._findPos(input);
361
+ $.timepicker._pos[1] += input.offsetHeight; // add the height
362
+ }
363
+ var isFixed = false;
364
+ $(input).parents().each(function () {
365
+ isFixed |= $(this).css('position') == 'fixed';
366
+ return !isFixed;
367
+ });
368
+
369
+ var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
370
+
371
+ $.timepicker._pos = null;
372
+ // determine sizing offscreen
373
+ inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
374
+ $.timepicker._updateTimepicker(inst);
375
+
376
+
377
+ // position with the ui position utility, if loaded
378
+ if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
379
+ inst.tpDiv.position({
380
+ of: inst.input,
381
+ my: $.timepicker._get( inst, 'myPosition' ),
382
+ at: $.timepicker._get( inst, 'atPosition' ),
383
+ // offset: $( "#offset" ).val(),
384
+ // using: using,
385
+ collision: 'flip'
386
+ });
387
+ var offset = inst.tpDiv.offset();
388
+ $.timepicker._pos = [offset.top, offset.left];
389
+ }
390
+
391
+
392
+ // reset clicked state
393
+ inst._hoursClicked = false;
394
+ inst._minutesClicked = false;
395
+
396
+ // fix width for dynamic number of time pickers
397
+ // and adjust position before showing
398
+ offset = $.timepicker._checkOffset(inst, offset, isFixed);
399
+ inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
400
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
401
+ left: offset.left + 'px', top: offset.top + 'px'
402
+ });
403
+ if ( ! inst.inline ) {
404
+ var showAnim = $.timepicker._get(inst, 'showAnim');
405
+ var duration = $.timepicker._get(inst, 'duration');
406
+
407
+ var postProcess = function () {
408
+ $.timepicker._timepickerShowing = true;
409
+ var borders = $.timepicker._getBorders(inst.tpDiv);
410
+ inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
411
+ css({ left: -borders[0], top: -borders[1],
412
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
413
+ });
414
+ };
415
+
416
+ // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
417
+ $.timepicker._adjustZIndex(input);
418
+ //inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
419
+
420
+ if ($.effects && $.effects[showAnim]) {
421
+ inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
422
+ }
423
+ else {
424
+ inst.tpDiv.show((showAnim ? duration : null), postProcess);
425
+ }
426
+ if (!showAnim || !duration) { postProcess(); }
427
+ if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
428
+ $.timepicker._curInst = inst;
429
+ }
430
+ },
431
+
432
+ // This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility.
433
+ // Enhancement returns maximum zindex value discovered while traversing parent elements,
434
+ // rather than the first zindex value found. Ensures the timepicker popup will be in front,
435
+ // even in funky scenarios like non-jq dialog containers with large fixed zindex values and
436
+ // nested zindex-influenced elements of their own.
437
+ _getZIndex: function (target) {
438
+ var elem = $(target);
439
+ var maxValue = 0;
440
+ var position, value;
441
+ while (elem.length && elem[0] !== document) {
442
+ position = elem.css("position");
443
+ if (position === "absolute" || position === "relative" || position === "fixed") {
444
+ value = parseInt(elem.css("zIndex"), 10);
445
+ if (!isNaN(value) && value !== 0) {
446
+ if (value > maxValue) { maxValue = value; }
447
+ }
448
+ }
449
+ elem = elem.parent();
450
+ }
451
+
452
+ return maxValue;
453
+ },
454
+
455
+ /* Refresh the time picker
456
+ @param target element - The target input field or inline container element. */
457
+ _refreshTimepicker: function(target) {
458
+ var inst = this._getInst(target);
459
+ if (inst) {
460
+ this._updateTimepicker(inst);
461
+ }
462
+ },
463
+
464
+
465
+ /* Generate the time picker content. */
466
+ _updateTimepicker: function (inst) {
467
+ inst.tpDiv.empty().append(this._generateHTML(inst));
468
+ this._rebindDialogEvents(inst);
469
+
470
+ },
471
+
472
+ _rebindDialogEvents: function (inst) {
473
+ var borders = $.timepicker._getBorders(inst.tpDiv),
474
+ self = this;
475
+ inst.tpDiv
476
+ .find('iframe.ui-timepicker-cover') // IE6- only
477
+ .css({ left: -borders[0], top: -borders[1],
478
+ width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
479
+ })
480
+ .end()
481
+ // after the picker html is appended bind the click & double click events (faster in IE this way
482
+ // then letting the browser interpret the inline events)
483
+ // the binding for the minute cells also exists in _updateMinuteDisplay
484
+ .find('.ui-timepicker-minute-cell')
485
+ .unbind()
486
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
487
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
488
+ .end()
489
+ .find('.ui-timepicker-hour-cell')
490
+ .unbind()
491
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
492
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
493
+ .end()
494
+ .find('.ui-timepicker td a')
495
+ .unbind()
496
+ .bind('mouseout', function () {
497
+ $(this).removeClass('ui-state-hover');
498
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
499
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
500
+ })
501
+ .bind('mouseover', function () {
502
+ if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
503
+ $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
504
+ $(this).addClass('ui-state-hover');
505
+ if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
506
+ if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
507
+ }
508
+ })
509
+ .end()
510
+ .find('.' + this._dayOverClass + ' a')
511
+ .trigger('mouseover')
512
+ .end()
513
+ .find('.ui-timepicker-now').bind("click", function(e) {
514
+ $.timepicker.selectNow(e);
515
+ }).end()
516
+ .find('.ui-timepicker-deselect').bind("click",function(e) {
517
+ $.timepicker.deselectTime(e);
518
+ }).end()
519
+ .find('.ui-timepicker-close').bind("click",function(e) {
520
+ $.timepicker._hideTimepicker();
521
+ }).end();
522
+ },
523
+
524
+ /* Generate the HTML for the current state of the time picker. */
525
+ _generateHTML: function (inst) {
526
+
527
+ var h, m, row, col, html, hoursHtml, minutesHtml = '',
528
+ showPeriod = (this._get(inst, 'showPeriod') == true),
529
+ showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
530
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
531
+ showHours = (this._get(inst, 'showHours') == true),
532
+ showMinutes = (this._get(inst, 'showMinutes') == true),
533
+ amPmText = this._get(inst, 'amPmText'),
534
+ rows = this._get(inst, 'rows'),
535
+ amRows = 0,
536
+ pmRows = 0,
537
+ amItems = 0,
538
+ pmItems = 0,
539
+ amFirstRow = 0,
540
+ pmFirstRow = 0,
541
+ hours = Array(),
542
+ hours_options = this._get(inst, 'hours'),
543
+ hoursPerRow = null,
544
+ hourCounter = 0,
545
+ hourLabel = this._get(inst, 'hourText'),
546
+ showCloseButton = this._get(inst, 'showCloseButton'),
547
+ closeButtonText = this._get(inst, 'closeButtonText'),
548
+ showNowButton = this._get(inst, 'showNowButton'),
549
+ nowButtonText = this._get(inst, 'nowButtonText'),
550
+ showDeselectButton = this._get(inst, 'showDeselectButton'),
551
+ deselectButtonText = this._get(inst, 'deselectButtonText'),
552
+ showButtonPanel = showCloseButton || showNowButton || showDeselectButton;
553
+
554
+
555
+
556
+ // prepare all hours and minutes, makes it easier to distribute by rows
557
+ for (h = hours_options.starts; h <= hours_options.ends; h++) {
558
+ hours.push (h);
559
+ }
560
+ hoursPerRow = Math.ceil(hours.length / rows); // always round up
561
+
562
+ if (showPeriodLabels) {
563
+ for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
564
+ if (hours[hourCounter] < 12) {
565
+ amItems++;
566
+ }
567
+ else {
568
+ pmItems++;
569
+ }
570
+ }
571
+ hourCounter = 0;
572
+
573
+ amRows = Math.floor(amItems / hours.length * rows);
574
+ pmRows = Math.floor(pmItems / hours.length * rows);
575
+
576
+ // assign the extra row to the period that is more densely populated
577
+ if (rows != amRows + pmRows) {
578
+ // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
579
+ if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
580
+ amRows++;
581
+ } else {
582
+ pmRows++;
583
+ }
584
+ }
585
+ amFirstRow = Math.min(amRows, 1);
586
+ pmFirstRow = amRows + 1;
587
+
588
+ if (amRows == 0) {
589
+ hoursPerRow = Math.ceil(pmItems / pmRows);
590
+ } else if (pmRows == 0) {
591
+ hoursPerRow = Math.ceil(amItems / amRows);
592
+ } else {
593
+ hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
594
+ }
595
+ }
596
+
597
+
598
+ html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';
599
+
600
+ if (showHours) {
601
+
602
+ html += '<td class="ui-timepicker-hours">' +
603
+ '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
604
+ hourLabel +
605
+ '</div>' +
606
+ '<table class="ui-timepicker">';
607
+
608
+ for (row = 1; row <= rows; row++) {
609
+ html += '<tr>';
610
+ // AM
611
+ if (row == amFirstRow && showPeriodLabels) {
612
+ html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
613
+ }
614
+ // PM
615
+ if (row == pmFirstRow && showPeriodLabels) {
616
+ html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
617
+ }
618
+ for (col = 1; col <= hoursPerRow; col++) {
619
+ if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
620
+ html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
621
+ } else {
622
+ html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
623
+ hourCounter++;
624
+ }
625
+ }
626
+ html += '</tr>';
627
+ }
628
+ html += '</table>' + // Close the hours cells table
629
+ '</td>'; // Close the Hour td
630
+ }
631
+
632
+ if (showMinutes) {
633
+ html += '<td class="ui-timepicker-minutes">';
634
+ html += this._generateHTMLMinutes(inst);
635
+ html += '</td>';
636
+ }
637
+
638
+ html += '</tr>';
639
+
640
+
641
+ if (showButtonPanel) {
642
+ var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
643
+ if (showNowButton) {
644
+ buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
645
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
646
+ + nowButtonText + '</button>';
647
+ }
648
+ if (showDeselectButton) {
649
+ buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
650
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
651
+ + deselectButtonText + '</button>';
652
+ }
653
+ if (showCloseButton) {
654
+ buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
655
+ + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
656
+ + closeButtonText + '</button>';
657
+ }
658
+
659
+ html += buttonPanel + '</div></td></tr>';
660
+ }
661
+ html += '</table>';
662
+
663
+ return html;
664
+ },
665
+
666
+ /* Special function that update the minutes selection in currently visible timepicker
667
+ * called on hour selection when onMinuteShow is defined */
668
+ _updateMinuteDisplay: function (inst) {
669
+ var newHtml = this._generateHTMLMinutes(inst);
670
+ inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
671
+ this._rebindDialogEvents(inst);
672
+ // after the picker html is appended bind the click & double click events (faster in IE this way
673
+ // then letting the browser interpret the inline events)
674
+ // yes I know, duplicate code, sorry
675
+ /* .find('.ui-timepicker-minute-cell')
676
+ .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
677
+ .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
678
+ */
679
+
680
+ },
681
+
682
+ /*
683
+ * Generate the minutes table
684
+ * This is separated from the _generateHTML function because is can be called separately (when hours changes)
685
+ */
686
+ _generateHTMLMinutes: function (inst) {
687
+
688
+ var m, row, html = '',
689
+ rows = this._get(inst, 'rows'),
690
+ minutes = Array(),
691
+ minutes_options = this._get(inst, 'minutes'),
692
+ minutesPerRow = null,
693
+ minuteCounter = 0,
694
+ showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
695
+ onMinuteShow = this._get(inst, 'onMinuteShow'),
696
+ minuteLabel = this._get(inst, 'minuteText');
697
+
698
+ if ( ! minutes_options.starts) {
699
+ minutes_options.starts = 0;
700
+ }
701
+ if ( ! minutes_options.ends) {
702
+ minutes_options.ends = 59;
703
+ }
704
+ if ( ! minutes_options.manual) {
705
+ minutes_options.manual = [];
706
+ }
707
+ for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
708
+ minutes.push(m);
709
+ }
710
+ for (i = 0; i < minutes_options.manual.length;i++) {
711
+ var currMin = minutes_options.manual[i];
712
+
713
+ // Validate & filter duplicates of manual minute input
714
+ if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray(currMin, minutes) >= 0) {
715
+ continue;
716
+ }
717
+ minutes.push(currMin);
718
+ }
719
+
720
+ // Sort to get correct order after adding manual minutes
721
+ // Use compare function to sort by number, instead of string (default)
722
+ minutes.sort(function(a, b) {
723
+ return a-b;
724
+ });
725
+
726
+ minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
727
+
728
+ /*
729
+ * The minutes table
730
+ */
731
+ // if currently selected minute is not enabled, we have a problem and need to select a new minute.
732
+ if (onMinuteShow &&
733
+ (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
734
+ // loop minutes and select first available
735
+ for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
736
+ m = minutes[minuteCounter];
737
+ if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
738
+ inst.minutes = m;
739
+ break;
740
+ }
741
+ }
742
+ }
743
+
744
+
745
+
746
+ html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
747
+ minuteLabel +
748
+ '</div>' +
749
+ '<table class="ui-timepicker">';
750
+
751
+ minuteCounter = 0;
752
+ for (row = 1; row <= rows; row++) {
753
+ html += '<tr>';
754
+ while (minuteCounter < row * minutesPerRow) {
755
+ var m = minutes[minuteCounter];
756
+ var displayText = '';
757
+ if (m !== undefined ) {
758
+ displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
759
+ }
760
+ html += this._generateHTMLMinuteCell(inst, m, displayText);
761
+ minuteCounter++;
762
+ }
763
+ html += '</tr>';
764
+ }
765
+
766
+ html += '</table>';
767
+
768
+ return html;
769
+ },
770
+
771
+ /* Generate the content of a "Hour" cell */
772
+ _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
773
+
774
+ var displayHour = hour;
775
+ if ((hour > 12) && showPeriod) {
776
+ displayHour = hour - 12;
777
+ }
778
+ if ((displayHour == 0) && showPeriod) {
779
+ displayHour = 12;
780
+ }
781
+ if ((displayHour < 10) && showLeadingZero) {
782
+ displayHour = '0' + displayHour;
783
+ }
784
+
785
+ var html = "";
786
+ var enabled = true;
787
+ var onHourShow = this._get(inst, 'onHourShow'); //custom callback
788
+ var maxTime = this._get(inst, 'maxTime');
789
+ var minTime = this._get(inst, 'minTime');
790
+
791
+ if (hour == undefined) {
792
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
793
+ return html;
794
+ }
795
+
796
+ if (onHourShow) {
797
+ enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
798
+ }
799
+
800
+ if (enabled) {
801
+ if ( !isNaN(parseInt(maxTime.hour)) && hour > maxTime.hour ) enabled = false;
802
+ if ( !isNaN(parseInt(minTime.hour)) && hour < minTime.hour ) enabled = false;
803
+ }
804
+
805
+ if (enabled) {
806
+ html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-hour="' + hour.toString() + '">' +
807
+ '<a class="ui-state-default ' +
808
+ (hour == inst.hours ? 'ui-state-active' : '') +
809
+ '">' +
810
+ displayHour.toString() +
811
+ '</a></td>';
812
+ }
813
+ else {
814
+ html =
815
+ '<td>' +
816
+ '<span class="ui-state-default ui-state-disabled ' +
817
+ (hour == inst.hours ? ' ui-state-active ' : ' ') +
818
+ '">' +
819
+ displayHour.toString() +
820
+ '</span>' +
821
+ '</td>';
822
+ }
823
+ return html;
824
+ },
825
+
826
+ /* Generate the content of a "Hour" cell */
827
+ _generateHTMLMinuteCell: function (inst, minute, displayText) {
828
+ var html = "";
829
+ var enabled = true;
830
+ var hour = inst.hours;
831
+ var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
832
+ var maxTime = this._get(inst, 'maxTime');
833
+ var minTime = this._get(inst, 'minTime');
834
+
835
+ if (onMinuteShow) {
836
+ //NEW: 2011-02-03 we should give the hour as a parameter as well!
837
+ enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
838
+ }
839
+
840
+ if (minute == undefined) {
841
+ html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
842
+ return html;
843
+ }
844
+
845
+ if (enabled && hour !== null) {
846
+ if ( !isNaN(parseInt(maxTime.hour)) && !isNaN(parseInt(maxTime.minute)) && hour >= maxTime.hour && minute > maxTime.minute ) enabled = false;
847
+ if ( !isNaN(parseInt(minTime.hour)) && !isNaN(parseInt(minTime.minute)) && hour <= minTime.hour && minute < minTime.minute ) enabled = false;
848
+ }
849
+
850
+ if (enabled) {
851
+ html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-minute="' + minute.toString() + '" >' +
852
+ '<a class="ui-state-default ' +
853
+ (minute == inst.minutes ? 'ui-state-active' : '') +
854
+ '" >' +
855
+ displayText +
856
+ '</a></td>';
857
+ }
858
+ else {
859
+
860
+ html = '<td>' +
861
+ '<span class="ui-state-default ui-state-disabled" >' +
862
+ displayText +
863
+ '</span>' +
864
+ '</td>';
865
+ }
866
+ return html;
867
+ },
868
+
869
+
870
+ /* Detach a timepicker from its control.
871
+ @param target element - the target input field or division or span */
872
+ _destroyTimepicker: function(target) {
873
+ var $target = $(target);
874
+ var inst = $.data(target, PROP_NAME);
875
+ if (!$target.hasClass(this.markerClassName)) {
876
+ return;
877
+ }
878
+ var nodeName = target.nodeName.toLowerCase();
879
+ $.removeData(target, PROP_NAME);
880
+ if (nodeName == 'input') {
881
+ inst.append.remove();
882
+ inst.trigger.remove();
883
+ $target.removeClass(this.markerClassName)
884
+ .unbind('focus.timepicker', this._showTimepicker)
885
+ .unbind('click.timepicker', this._adjustZIndex);
886
+ } else if (nodeName == 'div' || nodeName == 'span')
887
+ $target.removeClass(this.markerClassName).empty();
888
+ },
889
+
890
+ /* Enable the date picker to a jQuery selection.
891
+ @param target element - the target input field or division or span */
892
+ _enableTimepicker: function(target) {
893
+ var $target = $(target),
894
+ target_id = $target.attr('id'),
895
+ inst = $.data(target, PROP_NAME);
896
+
897
+ if (!$target.hasClass(this.markerClassName)) {
898
+ return;
899
+ }
900
+ var nodeName = target.nodeName.toLowerCase();
901
+ if (nodeName == 'input') {
902
+ target.disabled = false;
903
+ var button = this._get(inst, 'button');
904
+ $(button).removeClass('ui-state-disabled').disabled = false;
905
+ inst.trigger.filter('button').
906
+ each(function() { this.disabled = false; }).end();
907
+ }
908
+ else if (nodeName == 'div' || nodeName == 'span') {
909
+ var inline = $target.children('.' + this._inlineClass);
910
+ inline.children().removeClass('ui-state-disabled');
911
+ inline.find('button').each(
912
+ function() { this.disabled = false }
913
+ )
914
+ }
915
+ this._disabledInputs = $.map(this._disabledInputs,
916
+ function(value) { return (value == target_id ? null : value); }); // delete entry
917
+ },
918
+
919
+ /* Disable the time picker to a jQuery selection.
920
+ @param target element - the target input field or division or span */
921
+ _disableTimepicker: function(target) {
922
+ var $target = $(target);
923
+ var inst = $.data(target, PROP_NAME);
924
+ if (!$target.hasClass(this.markerClassName)) {
925
+ return;
926
+ }
927
+ var nodeName = target.nodeName.toLowerCase();
928
+ if (nodeName == 'input') {
929
+ var button = this._get(inst, 'button');
930
+
931
+ $(button).addClass('ui-state-disabled').disabled = true;
932
+ target.disabled = true;
933
+
934
+ inst.trigger.filter('button').
935
+ each(function() { this.disabled = true; }).end();
936
+
937
+ }
938
+ else if (nodeName == 'div' || nodeName == 'span') {
939
+ var inline = $target.children('.' + this._inlineClass);
940
+ inline.children().addClass('ui-state-disabled');
941
+ inline.find('button').each(
942
+ function() { this.disabled = true }
943
+ )
944
+
945
+ }
946
+ this._disabledInputs = $.map(this._disabledInputs,
947
+ function(value) { return (value == target ? null : value); }); // delete entry
948
+ this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
949
+ },
950
+
951
+ /* Is the first field in a jQuery collection disabled as a timepicker?
952
+ @param target_id element - the target input field or division or span
953
+ @return boolean - true if disabled, false if enabled */
954
+ _isDisabledTimepicker: function (target_id) {
955
+ if ( ! target_id) { return false; }
956
+ for (var i = 0; i < this._disabledInputs.length; i++) {
957
+ if (this._disabledInputs[i] == target_id) { return true; }
958
+ }
959
+ return false;
960
+ },
961
+
962
+ /* Check positioning to remain on screen. */
963
+ _checkOffset: function (inst, offset, isFixed) {
964
+ var tpWidth = inst.tpDiv.outerWidth();
965
+ var tpHeight = inst.tpDiv.outerHeight();
966
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
967
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
968
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
969
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
970
+
971
+ offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
972
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
973
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
974
+
975
+ // now check if timepicker is showing outside window viewport - move to a better place if so.
976
+ offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
977
+ Math.abs(offset.left + tpWidth - viewWidth) : 0);
978
+ offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
979
+ Math.abs(tpHeight + inputHeight) : 0);
980
+
981
+ return offset;
982
+ },
983
+
984
+ /* Find an object's position on the screen. */
985
+ _findPos: function (obj) {
986
+ var inst = this._getInst(obj);
987
+ var isRTL = this._get(inst, 'isRTL');
988
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
989
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
990
+ }
991
+ var position = $(obj).offset();
992
+ return [position.left, position.top];
993
+ },
994
+
995
+ /* Retrieve the size of left and top borders for an element.
996
+ @param elem (jQuery object) the element of interest
997
+ @return (number[2]) the left and top borders */
998
+ _getBorders: function (elem) {
999
+ var convert = function (value) {
1000
+ return { thin: 1, medium: 2, thick: 3}[value] || value;
1001
+ };
1002
+ return [parseFloat(convert(elem.css('border-left-width'))),
1003
+ parseFloat(convert(elem.css('border-top-width')))];
1004
+ },
1005
+
1006
+
1007
+ /* Close time picker if clicked elsewhere. */
1008
+ _checkExternalClick: function (event) {
1009
+ if (!$.timepicker._curInst) { return; }
1010
+ var $target = $(event.target);
1011
+ if ($target[0].id != $.timepicker._mainDivId &&
1012
+ $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
1013
+ !$target.hasClass($.timepicker.markerClassName) &&
1014
+ !$target.hasClass($.timepicker._triggerClass) &&
1015
+ $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
1016
+ $.timepicker._hideTimepicker();
1017
+ },
1018
+
1019
+ /* Hide the time picker from view.
1020
+ @param input element - the input field attached to the time picker */
1021
+ _hideTimepicker: function (input) {
1022
+ var inst = this._curInst;
1023
+ if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1024
+ if (this._timepickerShowing) {
1025
+ var showAnim = this._get(inst, 'showAnim');
1026
+ var duration = this._get(inst, 'duration');
1027
+ var postProcess = function () {
1028
+ $.timepicker._tidyDialog(inst);
1029
+ this._curInst = null;
1030
+ };
1031
+ if ($.effects && $.effects[showAnim]) {
1032
+ inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
1033
+ }
1034
+ else {
1035
+ inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
1036
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
1037
+ }
1038
+ if (!showAnim) { postProcess(); }
1039
+
1040
+ this._timepickerShowing = false;
1041
+
1042
+ this._lastInput = null;
1043
+ if (this._inDialog) {
1044
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
1045
+ if ($.blockUI) {
1046
+ $.unblockUI();
1047
+ $('body').append(this.tpDiv);
1048
+ }
1049
+ }
1050
+ this._inDialog = false;
1051
+
1052
+ var onClose = this._get(inst, 'onClose');
1053
+ if (onClose) {
1054
+ onClose.apply(
1055
+ (inst.input ? inst.input[0] : null),
1056
+ [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
1057
+ }
1058
+
1059
+ }
1060
+ },
1061
+
1062
+
1063
+
1064
+ /* Tidy up after a dialog display. */
1065
+ _tidyDialog: function (inst) {
1066
+ inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
1067
+ },
1068
+
1069
+ /* Retrieve the instance data for the target control.
1070
+ @param target element - the target input field or division or span
1071
+ @return object - the associated instance data
1072
+ @throws error if a jQuery problem getting data */
1073
+ _getInst: function (target) {
1074
+ try {
1075
+ return $.data(target, PROP_NAME);
1076
+ }
1077
+ catch (err) {
1078
+ throw 'Missing instance data for this timepicker';
1079
+ }
1080
+ },
1081
+
1082
+ /* Get a setting value, defaulting if necessary. */
1083
+ _get: function (inst, name) {
1084
+ return inst.settings[name] !== undefined ?
1085
+ inst.settings[name] : this._defaults[name];
1086
+ },
1087
+
1088
+ /* Parse existing time and initialise time picker. */
1089
+ _setTimeFromField: function (inst) {
1090
+ if (inst.input.val() == inst.lastVal) { return; }
1091
+ var defaultTime = this._get(inst, 'defaultTime');
1092
+
1093
+ var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
1094
+ if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
1095
+
1096
+ if (timeToParse instanceof Date) {
1097
+ inst.hours = timeToParse.getHours();
1098
+ inst.minutes = timeToParse.getMinutes();
1099
+ } else {
1100
+ var timeVal = inst.lastVal = timeToParse;
1101
+ if (timeToParse == '') {
1102
+ inst.hours = -1;
1103
+ inst.minutes = -1;
1104
+ } else {
1105
+ var time = this.parseTime(inst, timeVal);
1106
+ inst.hours = time.hours;
1107
+ inst.minutes = time.minutes;
1108
+ }
1109
+ }
1110
+
1111
+
1112
+ $.timepicker._updateTimepicker(inst);
1113
+ },
1114
+
1115
+ /* Update or retrieve the settings for an existing time picker.
1116
+ @param target element - the target input field or division or span
1117
+ @param name object - the new settings to update or
1118
+ string - the name of the setting to change or retrieve,
1119
+ when retrieving also 'all' for all instance settings or
1120
+ 'defaults' for all global defaults
1121
+ @param value any - the new value for the setting
1122
+ (omit if above is an object or to retrieve a value) */
1123
+ _optionTimepicker: function(target, name, value) {
1124
+ var inst = this._getInst(target);
1125
+ if (arguments.length == 2 && typeof name == 'string') {
1126
+ return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
1127
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
1128
+ this._get(inst, name)) : null));
1129
+ }
1130
+ var settings = name || {};
1131
+ if (typeof name == 'string') {
1132
+ settings = {};
1133
+ settings[name] = value;
1134
+ }
1135
+ if (inst) {
1136
+ extendRemove(inst.settings, settings);
1137
+ if (this._curInst == inst) {
1138
+ this._hideTimepicker();
1139
+ this._updateTimepicker(inst);
1140
+ }
1141
+ if (inst.inline) {
1142
+ this._updateTimepicker(inst);
1143
+ }
1144
+ }
1145
+ },
1146
+
1147
+
1148
+ /* Set the time for a jQuery selection.
1149
+ @param target element - the target input field or division or span
1150
+ @param time String - the new time */
1151
+ _setTimeTimepicker: function(target, time) {
1152
+ var inst = this._getInst(target);
1153
+ if (inst) {
1154
+ this._setTime(inst, time);
1155
+ this._updateTimepicker(inst);
1156
+ this._updateAlternate(inst, time);
1157
+ }
1158
+ },
1159
+
1160
+ /* Set the time directly. */
1161
+ _setTime: function(inst, time, noChange) {
1162
+ var origHours = inst.hours;
1163
+ var origMinutes = inst.minutes;
1164
+ if (time instanceof Date) {
1165
+ inst.hours = time.getHours();
1166
+ inst.minutes = time.getMinutes();
1167
+ } else {
1168
+ var time = this.parseTime(inst, time);
1169
+ inst.hours = time.hours;
1170
+ inst.minutes = time.minutes;
1171
+ }
1172
+
1173
+ if ((origHours != inst.hours || origMinutes != inst.minutes) && !noChange) {
1174
+ inst.input.trigger('change');
1175
+ }
1176
+ this._updateTimepicker(inst);
1177
+ this._updateSelectedValue(inst);
1178
+ },
1179
+
1180
+ /* Return the current time, ready to be parsed, rounded to the closest minute by interval */
1181
+ _getCurrentTimeRounded: function (inst) {
1182
+ var currentTime = new Date(),
1183
+ currentMinutes = currentTime.getMinutes(),
1184
+ minutes_options = this._get(inst, 'minutes'),
1185
+ // round to closest interval
1186
+ adjustedMinutes = Math.round(currentMinutes / minutes_options.interval) * minutes_options.interval;
1187
+ currentTime.setMinutes(adjustedMinutes);
1188
+ return currentTime;
1189
+ },
1190
+
1191
+ /*
1192
+ * Parse a time string into hours and minutes
1193
+ */
1194
+ parseTime: function (inst, timeVal) {
1195
+ var retVal = new Object();
1196
+ retVal.hours = -1;
1197
+ retVal.minutes = -1;
1198
+
1199
+ if(!timeVal)
1200
+ return '';
1201
+
1202
+ var timeSeparator = this._get(inst, 'timeSeparator'),
1203
+ amPmText = this._get(inst, 'amPmText'),
1204
+ showHours = this._get(inst, 'showHours'),
1205
+ showMinutes = this._get(inst, 'showMinutes'),
1206
+ optionalMinutes = this._get(inst, 'optionalMinutes'),
1207
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1208
+ p = timeVal.indexOf(timeSeparator);
1209
+
1210
+ // check if time separator found
1211
+ if (p != -1) {
1212
+ retVal.hours = parseInt(timeVal.substr(0, p), 10);
1213
+ retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
1214
+ }
1215
+ // check for hours only
1216
+ else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) {
1217
+ retVal.hours = parseInt(timeVal, 10);
1218
+ }
1219
+ // check for minutes only
1220
+ else if ( ( ! showHours) && (showMinutes) ) {
1221
+ retVal.minutes = parseInt(timeVal, 10);
1222
+ }
1223
+
1224
+ if (showHours) {
1225
+ var timeValUpper = timeVal.toUpperCase();
1226
+ if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
1227
+ retVal.hours += 12;
1228
+ }
1229
+ // fix for 12 AM
1230
+ if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
1231
+ retVal.hours = 0;
1232
+ }
1233
+ }
1234
+
1235
+ return retVal;
1236
+ },
1237
+
1238
+ selectNow: function(event) {
1239
+ var id = $(event.target).attr("data-timepicker-instance-id"),
1240
+ $target = $(id),
1241
+ inst = this._getInst($target[0]);
1242
+ //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
1243
+ var currentTime = new Date();
1244
+ inst.hours = currentTime.getHours();
1245
+ inst.minutes = currentTime.getMinutes();
1246
+ this._updateSelectedValue(inst);
1247
+ this._updateTimepicker(inst);
1248
+ this._hideTimepicker();
1249
+ },
1250
+
1251
+ deselectTime: function(event) {
1252
+ var id = $(event.target).attr("data-timepicker-instance-id"),
1253
+ $target = $(id),
1254
+ inst = this._getInst($target[0]);
1255
+ inst.hours = -1;
1256
+ inst.minutes = -1;
1257
+ this._updateSelectedValue(inst);
1258
+ this._hideTimepicker();
1259
+ },
1260
+
1261
+
1262
+ selectHours: function (event) {
1263
+ var $td = $(event.currentTarget),
1264
+ id = $td.attr("data-timepicker-instance-id"),
1265
+ newHours = parseInt($td.attr("data-hour")),
1266
+ fromDoubleClick = event.data.fromDoubleClick,
1267
+ $target = $(id),
1268
+ inst = this._getInst($target[0]),
1269
+ showMinutes = (this._get(inst, 'showMinutes') == true);
1270
+
1271
+ // don't select if disabled
1272
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1273
+
1274
+ $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
1275
+ $td.children('a').addClass('ui-state-active');
1276
+ inst.hours = newHours;
1277
+
1278
+ // added for onMinuteShow callback
1279
+ var onMinuteShow = this._get(inst, 'onMinuteShow'),
1280
+ maxTime = this._get(inst, 'maxTime'),
1281
+ minTime = this._get(inst, 'minTime');
1282
+ if (onMinuteShow || maxTime.minute || minTime.minute) {
1283
+ // this will trigger a callback on selected hour to make sure selected minute is allowed.
1284
+ this._updateMinuteDisplay(inst);
1285
+ }
1286
+
1287
+ this._updateSelectedValue(inst);
1288
+
1289
+ inst._hoursClicked = true;
1290
+ if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
1291
+ $.timepicker._hideTimepicker();
1292
+ }
1293
+ // return false because if used inline, prevent the url to change to a hashtag
1294
+ return false;
1295
+ },
1296
+
1297
+ selectMinutes: function (event) {
1298
+ var $td = $(event.currentTarget),
1299
+ id = $td.attr("data-timepicker-instance-id"),
1300
+ newMinutes = parseInt($td.attr("data-minute")),
1301
+ fromDoubleClick = event.data.fromDoubleClick,
1302
+ $target = $(id),
1303
+ inst = this._getInst($target[0]),
1304
+ showHours = (this._get(inst, 'showHours') == true);
1305
+
1306
+ // don't select if disabled
1307
+ if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
1308
+
1309
+ $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
1310
+ $td.children('a').addClass('ui-state-active');
1311
+
1312
+ inst.minutes = newMinutes;
1313
+ this._updateSelectedValue(inst);
1314
+
1315
+ inst._minutesClicked = true;
1316
+ if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
1317
+ $.timepicker._hideTimepicker();
1318
+ // return false because if used inline, prevent the url to change to a hashtag
1319
+ return false;
1320
+ }
1321
+
1322
+ // return false because if used inline, prevent the url to change to a hashtag
1323
+ return false;
1324
+ },
1325
+
1326
+ _updateSelectedValue: function (inst) {
1327
+ var newTime = this._getParsedTime(inst);
1328
+ if (inst.input) {
1329
+ inst.input.val(newTime);
1330
+ inst.input.trigger('change');
1331
+ }
1332
+ var onSelect = this._get(inst, 'onSelect');
1333
+ if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
1334
+ this._updateAlternate(inst, newTime);
1335
+ return newTime;
1336
+ },
1337
+
1338
+ /* this function process selected time and return it parsed according to instance options */
1339
+ _getParsedTime: function(inst) {
1340
+
1341
+ if (inst.hours == -1 && inst.minutes == -1) {
1342
+ return '';
1343
+ }
1344
+
1345
+ // default to 0 AM if hours is not valid
1346
+ if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
1347
+ // default to 0 minutes if minute is not valid
1348
+ if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
1349
+
1350
+ var period = "",
1351
+ showPeriod = (this._get(inst, 'showPeriod') == true),
1352
+ showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
1353
+ showHours = (this._get(inst, 'showHours') == true),
1354
+ showMinutes = (this._get(inst, 'showMinutes') == true),
1355
+ optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
1356
+ amPmText = this._get(inst, 'amPmText'),
1357
+ selectedHours = inst.hours ? inst.hours : 0,
1358
+ selectedMinutes = inst.minutes ? inst.minutes : 0,
1359
+ displayHours = selectedHours ? selectedHours : 0,
1360
+ parsedTime = '';
1361
+
1362
+ // fix some display problem when hours or minutes are not selected yet
1363
+ if (displayHours == -1) { displayHours = 0 }
1364
+ if (selectedMinutes == -1) { selectedMinutes = 0 }
1365
+
1366
+ if (showPeriod) {
1367
+ if (inst.hours == 0) {
1368
+ displayHours = 12;
1369
+ }
1370
+ if (inst.hours < 12) {
1371
+ period = amPmText[0];
1372
+ }
1373
+ else {
1374
+ period = amPmText[1];
1375
+ if (displayHours > 12) {
1376
+ displayHours -= 12;
1377
+ }
1378
+ }
1379
+ }
1380
+
1381
+ var h = displayHours.toString();
1382
+ if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
1383
+
1384
+ var m = selectedMinutes.toString();
1385
+ if (selectedMinutes < 10) { m = '0' + m; }
1386
+
1387
+ if (showHours) {
1388
+ parsedTime += h;
1389
+ }
1390
+ if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
1391
+ parsedTime += this._get(inst, 'timeSeparator');
1392
+ }
1393
+ if (showMinutes && (!optionalMinutes || m != 0)) {
1394
+ parsedTime += m;
1395
+ }
1396
+ if (showHours) {
1397
+ if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
1398
+ }
1399
+
1400
+ return parsedTime;
1401
+ },
1402
+
1403
+ /* Update any alternate field to synchronise with the main field. */
1404
+ _updateAlternate: function(inst, newTime) {
1405
+ var altField = this._get(inst, 'altField');
1406
+ if (altField) { // update alternate field too
1407
+ $(altField).each(function(i,e) {
1408
+ $(e).val(newTime);
1409
+ });
1410
+ }
1411
+ },
1412
+
1413
+ _getTimeAsDateTimepicker: function(input) {
1414
+ var inst = this._getInst(input);
1415
+ if (inst.hours == -1 && inst.minutes == -1) {
1416
+ return '';
1417
+ }
1418
+
1419
+ // default to 0 AM if hours is not valid
1420
+ if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
1421
+ // default to 0 minutes if minute is not valid
1422
+ if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
1423
+
1424
+ return new Date(0, 0, 0, inst.hours, inst.minutes, 0);
1425
+ },
1426
+ /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
1427
+ /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
1428
+ _getTimeTimepicker : function(input) {
1429
+ var inst = this._getInst(input);
1430
+ return this._getParsedTime(inst);
1431
+ },
1432
+ _getHourTimepicker: function(input) {
1433
+ var inst = this._getInst(input);
1434
+ if ( inst == undefined) { return -1; }
1435
+ return inst.hours;
1436
+ },
1437
+ _getMinuteTimepicker: function(input) {
1438
+ var inst= this._getInst(input);
1439
+ if ( inst == undefined) { return -1; }
1440
+ return inst.minutes;
1441
+ }
1442
+
1443
+ });
1444
+
1445
+
1446
+
1447
+ /* Invoke the timepicker functionality.
1448
+ @param options string - a command, optionally followed by additional parameters or
1449
+ Object - settings for attaching new timepicker functionality
1450
+ @return jQuery object */
1451
+ $.fn.timepicker = function (options) {
1452
+ /* Initialise the time picker. */
1453
+ if (!$.timepicker.initialized) {
1454
+ $(document).mousedown($.timepicker._checkExternalClick);
1455
+ $.timepicker.initialized = true;
1456
+ }
1457
+
1458
+ /* Append timepicker main container to body if not exist. */
1459
+ if ($("#"+$.timepicker._mainDivId).length === 0) {
1460
+ $('body').append($.timepicker.tpDiv);
1461
+ }
1462
+
1463
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
1464
+ if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute' ))
1465
+ return $.timepicker['_' + options + 'Timepicker'].
1466
+ apply($.timepicker, [this[0]].concat(otherArgs));
1467
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
1468
+ return $.timepicker['_' + options + 'Timepicker'].
1469
+ apply($.timepicker, [this[0]].concat(otherArgs));
1470
+ return this.each(function () {
1471
+ typeof options == 'string' ?
1472
+ $.timepicker['_' + options + 'Timepicker'].
1473
+ apply($.timepicker, [this].concat(otherArgs)) :
1474
+ $.timepicker._attachTimepicker(this, options);
1475
+ });
1476
+ };
1477
+
1478
+ /* jQuery extend now ignores nulls! */
1479
+ function extendRemove(target, props) {
1480
+ $.extend(target, props);
1481
+ for (var name in props)
1482
+ if (props[name] == null || props[name] == undefined)
1483
+ target[name] = props[name];
1484
+ return target;
1485
+ };
1486
+
1487
+ $.timepicker = new Timepicker(); // singleton instance
1488
+ $.timepicker.initialized = false;
1489
+ $.timepicker.uuid = new Date().getTime();
1490
+ $.timepicker.version = "0.3.3";
1491
+
1492
+ // Workaround for #4055
1493
+ // Add another global to avoid noConflict issues with inline event handlers
1494
+ window['TP_jQuery_' + tpuuid] = $;
1495
+
1496
+ })(jQuery);
media/js/lib/jquery.ui.timepicker.min.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI Timepicker
3
+ *
4
+ * Copyright 2010-2013, Francois Gelinas
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://fgelinas.com/code/timepicker
9
+ *
10
+ * Depends:
11
+ * jquery.ui.core.js
12
+ * jquery.ui.position.js (only if position settings are used)
13
+ *
14
+ * Change version 0.1.0 - moved the t-rex up here
15
+ *
16
+ ____
17
+ ___ .-~. /_"-._
18
+ `-._~-. / /_ "~o\ :Y
19
+ \ \ / : \~x. ` ')
20
+ ] Y / | Y< ~-.__j
21
+ / ! _.--~T : l l< /.-~
22
+ / / ____.--~ . ` l /~\ \<|Y
23
+ / / .-~~" /| . ',-~\ \L|
24
+ / / / .^ \ Y~Y \.^>/l_ "--'
25
+ / Y .-"( . l__ j_j l_/ /~_.-~ .
26
+ Y l / \ ) ~~~." / `/"~ / \.__/l_
27
+ | \ _.-" ~-{__ l : l._Z~-.___.--~
28
+ | ~---~ / ~~"---\_ ' __[>
29
+ l . _.^ ___ _>-y~
30
+ \ \ . .-~ .-~ ~>--" /
31
+ \ ~---" / ./ _.-'
32
+ "-.,_____.,_ _.--~\ _.-~
33
+ ~~ ( _} -Row
34
+ `. ~(
35
+ ) \
36
+ /,`--'~\--'~\
37
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38
+ ->T-Rex<-
39
+ */
40
+ !function($){function Timepicker(){this.debug=!0,this._curInst=null,this._disabledInputs=[],this._timepickerShowing=!1,this._inDialog=!1,this._dialogClass="ui-timepicker-dialog",this._mainDivId="ui-timepicker-div",this._inlineClass="ui-timepicker-inline",this._currentClass="ui-timepicker-current",this._dayOverClass="ui-timepicker-days-cell-over",this.regional=[],this.regional[""]={hourText:"Hour",minuteText:"Minute",amPmText:["AM","PM"],closeButtonText:"Done",nowButtonText:"Now",deselectButtonText:"Deselect"},this._defaults={showOn:"focus",button:null,showAnim:"fadeIn",showOptions:{},appendText:"",beforeShow:null,onSelect:null,onClose:null,timeSeparator:":",periodSeparator:" ",showPeriod:!1,showPeriodLabels:!0,showLeadingZero:!0,showMinutesLeadingZero:!0,altField:"",defaultTime:"now",myPosition:"left top",atPosition:"left bottom",onHourShow:null,onMinuteShow:null,hours:{starts:0,ends:23},minutes:{starts:0,ends:55,interval:5,manual:[]},rows:4,showHours:!0,showMinutes:!0,optionalMinutes:!1,showCloseButton:!1,showNowButton:!1,showDeselectButton:!1,maxTime:{hour:null,minute:null},minTime:{hour:null,minute:null}},$.extend(this._defaults,this.regional[""]),this.tpDiv=$('<div id="'+this._mainDivId+'" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>')}function extendRemove(e,t){$.extend(e,t);for(var i in t)(null==t[i]||void 0==t[i])&&(e[i]=t[i]);return e}$.extend($.ui,{timepicker:{version:"0.3.3"}});var PROP_NAME="timepicker",tpuuid=(new Date).getTime();$.extend(Timepicker.prototype,{markerClassName:"hasTimepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetTimepicker:function(){return this.tpDiv},setDefaults:function(e){return extendRemove(this._defaults,e||{}),this},_attachTimepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("time:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline="div"==nodeName||"span"==nodeName;target.id||(this.uuid+=1,target.id="tp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),"input"==nodeName?(this._connectTimepicker(target,inst),this._setTimeFromField(inst)):inline&&this._inlineTimepicker(target,inst)},_newInst:function(e,t){var i=e[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:i,input:e,inline:t,tpDiv:t?$('<div class="'+this._inlineClass+' ui-timepicker ui-widget ui-helper-clearfix"></div>'):this.tpDiv}},_connectTimepicker:function(e,t){var i=$(e);t.append=$([]),t.trigger=$([]),i.hasClass(this.markerClassName)||(this._attachments(i,t),i.addClass(this.markerClassName).keydown(this._doKeyDown).keyup(this._doKeyUp).bind("setData.timepicker",function(e,i,s){t.settings[i]=s}).bind("getData.timepicker",function(e,i){return this._get(t,i)}),$.data(e,PROP_NAME,t))},_doKeyDown:function(e){var t=$.timepicker._getInst(e.target),i=!0;if(t._keyEvent=!0,$.timepicker._timepickerShowing)switch(e.keyCode){case 9:$.timepicker._hideTimepicker(),i=!1;break;case 13:return $.timepicker._updateSelectedValue(t),$.timepicker._hideTimepicker(),!1;case 27:$.timepicker._hideTimepicker();break;default:i=!1}else 36==e.keyCode&&e.ctrlKey?$.timepicker._showTimepicker(this):i=!1;i&&(e.preventDefault(),e.stopPropagation())},_doKeyUp:function(e){var t=$.timepicker._getInst(e.target);$.timepicker._setTimeFromField(t),$.timepicker._updateTimepicker(t)},_attachments:function(e,t){var i=this._get(t,"appendText"),s=this._get(t,"isRTL");t.append&&t.append.remove(),i&&(t.append=$('<span class="'+this._appendClass+'">'+i+"</span>"),e[s?"before":"after"](t.append)),e.unbind("focus.timepicker",this._showTimepicker),e.unbind("click.timepicker",this._adjustZIndex),t.trigger&&t.trigger.remove();var n=this._get(t,"showOn");if(("focus"==n||"both"==n)&&(e.bind("focus.timepicker",this._showTimepicker),e.bind("click.timepicker",this._adjustZIndex)),"button"==n||"both"==n){var r=this._get(t,"button");null==r&&(r=$('<button class="ui-timepicker-trigger" type="button">...</button>'),e.after(r)),$(r).bind("click.timepicker",function(){return $.timepicker._timepickerShowing&&$.timepicker._lastInput==e[0]?$.timepicker._hideTimepicker():t.input.is(":disabled")||$.timepicker._showTimepicker(e[0]),!1})}},_inlineTimepicker:function(e,t){var i=$(e);i.hasClass(this.markerClassName)||(i.addClass(this.markerClassName).append(t.tpDiv).bind("setData.timepicker",function(e,i,s){t.settings[i]=s}).bind("getData.timepicker",function(e,i){return this._get(t,i)}),$.data(e,PROP_NAME,t),this._setTimeFromField(t),this._updateTimepicker(t),t.tpDiv.show())},_adjustZIndex:function(e){e=e.target||e;var t=$.timepicker._getInst(e);t.tpDiv.css("zIndex",$.timepicker._getZIndex(e)+1)},_showTimepicker:function(e){if(e=e.target||e,"input"!=e.nodeName.toLowerCase()&&(e=$("input",e.parentNode)[0]),!$.timepicker._isDisabledTimepicker(e)&&$.timepicker._lastInput!=e){$.timepicker._hideTimepicker();var t=$.timepicker._getInst(e);$.timepicker._curInst&&$.timepicker._curInst!=t&&$.timepicker._curInst.tpDiv.stop(!0,!0);var i=$.timepicker._get(t,"beforeShow");extendRemove(t.settings,i?i.apply(e,[e,t]):{}),t.lastVal=null,$.timepicker._lastInput=e,$.timepicker._setTimeFromField(t),$.timepicker._inDialog&&(e.value=""),$.timepicker._pos||($.timepicker._pos=$.timepicker._findPos(e),$.timepicker._pos[1]+=e.offsetHeight);var s=!1;$(e).parents().each(function(){return s|="fixed"==$(this).css("position"),!s});var n={left:$.timepicker._pos[0],top:$.timepicker._pos[1]};if($.timepicker._pos=null,t.tpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.timepicker._updateTimepicker(t),!t.inline&&"object"==typeof $.ui.position){t.tpDiv.position({of:t.input,my:$.timepicker._get(t,"myPosition"),at:$.timepicker._get(t,"atPosition"),collision:"flip"});var n=t.tpDiv.offset();$.timepicker._pos=[n.top,n.left]}if(t._hoursClicked=!1,t._minutesClicked=!1,n=$.timepicker._checkOffset(t,n,s),t.tpDiv.css({position:$.timepicker._inDialog&&$.blockUI?"static":s?"fixed":"absolute",display:"none",left:n.left+"px",top:n.top+"px"}),!t.inline){var r=$.timepicker._get(t,"showAnim"),a=$.timepicker._get(t,"duration"),u=function(){$.timepicker._timepickerShowing=!0;var e=$.timepicker._getBorders(t.tpDiv);t.tpDiv.find("iframe.ui-timepicker-cover").css({left:-e[0],top:-e[1],width:t.tpDiv.outerWidth(),height:t.tpDiv.outerHeight()})};$.timepicker._adjustZIndex(e),$.effects&&$.effects[r]?t.tpDiv.show(r,$.timepicker._get(t,"showOptions"),a,u):t.tpDiv.show(r?a:null,u),r&&a||u(),t.input.is(":visible")&&!t.input.is(":disabled")&&t.input.focus(),$.timepicker._curInst=t}}},_getZIndex:function(e){for(var t,i,s=$(e),n=0;s.length&&s[0]!==document;)t=s.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(i=parseInt(s.css("zIndex"),10),isNaN(i)||0===i||i>n&&(n=i)),s=s.parent();return n},_refreshTimepicker:function(e){var t=this._getInst(e);t&&this._updateTimepicker(t)},_updateTimepicker:function(e){e.tpDiv.empty().append(this._generateHTML(e)),this._rebindDialogEvents(e)},_rebindDialogEvents:function(e){var t=$.timepicker._getBorders(e.tpDiv),i=this;e.tpDiv.find("iframe.ui-timepicker-cover").css({left:-t[0],top:-t[1],width:e.tpDiv.outerWidth(),height:e.tpDiv.outerHeight()}).end().find(".ui-timepicker-minute-cell").unbind().bind("click",{fromDoubleClick:!1},$.proxy($.timepicker.selectMinutes,this)).bind("dblclick",{fromDoubleClick:!0},$.proxy($.timepicker.selectMinutes,this)).end().find(".ui-timepicker-hour-cell").unbind().bind("click",{fromDoubleClick:!1},$.proxy($.timepicker.selectHours,this)).bind("dblclick",{fromDoubleClick:!0},$.proxy($.timepicker.selectHours,this)).end().find(".ui-timepicker td a").unbind().bind("mouseout",function(){$(this).removeClass("ui-state-hover"),-1!=this.className.indexOf("ui-timepicker-prev")&&$(this).removeClass("ui-timepicker-prev-hover"),-1!=this.className.indexOf("ui-timepicker-next")&&$(this).removeClass("ui-timepicker-next-hover")}).bind("mouseover",function(){i._isDisabledTimepicker(e.inline?e.tpDiv.parent()[0]:e.input[0])||($(this).parents(".ui-timepicker-calendar").find("a").removeClass("ui-state-hover"),$(this).addClass("ui-state-hover"),-1!=this.className.indexOf("ui-timepicker-prev")&&$(this).addClass("ui-timepicker-prev-hover"),-1!=this.className.indexOf("ui-timepicker-next")&&$(this).addClass("ui-timepicker-next-hover"))}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end().find(".ui-timepicker-now").bind("click",function(e){$.timepicker.selectNow(e)}).end().find(".ui-timepicker-deselect").bind("click",function(e){$.timepicker.deselectTime(e)}).end().find(".ui-timepicker-close").bind("click",function(e){$.timepicker._hideTimepicker()}).end()},_generateHTML:function(e){var t,i,s,n,r=1==this._get(e,"showPeriod"),a=1==this._get(e,"showPeriodLabels"),u=1==this._get(e,"showLeadingZero"),o=1==this._get(e,"showHours"),c=1==this._get(e,"showMinutes"),p=this._get(e,"amPmText"),l=this._get(e,"rows"),d=0,h=0,m=0,_=0,g=0,k=0,f=Array(),v=this._get(e,"hours"),$=null,b=0,T=this._get(e,"hourText"),w=this._get(e,"showCloseButton"),C=this._get(e,"closeButtonText"),D=this._get(e,"showNowButton"),I=this._get(e,"nowButtonText"),x=this._get(e,"showDeselectButton"),M=this._get(e,"deselectButtonText"),y=w||D||x;for(t=v.starts;t<=v.ends;t++)f.push(t);if($=Math.ceil(f.length/l),a){for(b=0;b<f.length;b++)f[b]<12?m++:_++;b=0,d=Math.floor(m/f.length*l),h=Math.floor(_/f.length*l),l!=d+h&&(m&&(!_||!d||h&&m/d>=_/h)?d++:h++),g=Math.min(d,1),k=d+1,$=0==d?Math.ceil(_/h):0==h?Math.ceil(m/d):Math.ceil(Math.max(m/d,_/h))}if(n='<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>',o){for(n+='<td class="ui-timepicker-hours"><div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">'+T+'</div><table class="ui-timepicker">',i=1;l>=i;i++){for(n+="<tr>",i==g&&a&&(n+='<th rowspan="'+d.toString()+'" class="periods" scope="row">'+p[0]+"</th>"),i==k&&a&&(n+='<th rowspan="'+h.toString()+'" class="periods" scope="row">'+p[1]+"</th>"),s=1;$>=s;s++)a&&k>i&&f[b]>=12?n+=this._generateHTMLHourCell(e,void 0,r,u):(n+=this._generateHTMLHourCell(e,f[b],r,u),b++);n+="</tr>"}n+="</table></td>"}if(c&&(n+='<td class="ui-timepicker-minutes">',n+=this._generateHTMLMinutes(e),n+="</td>"),n+="</tr>",y){var N='<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';D&&(N+='<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" data-timepicker-instance-id="#'+e.id.replace(/\\\\/g,"\\")+'" >'+I+"</button>"),x&&(N+='<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" data-timepicker-instance-id="#'+e.id.replace(/\\\\/g,"\\")+'" >'+M+"</button>"),w&&(N+='<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" data-timepicker-instance-id="#'+e.id.replace(/\\\\/g,"\\")+'" >'+C+"</button>"),n+=N+"</div></td></tr>"}return n+="</table>"},_updateMinuteDisplay:function(e){var t=this._generateHTMLMinutes(e);e.tpDiv.find("td.ui-timepicker-minutes").html(t),this._rebindDialogEvents(e)},_generateHTMLMinutes:function(e){var t,s,n="",r=this._get(e,"rows"),a=Array(),u=this._get(e,"minutes"),o=null,c=0,p=1==this._get(e,"showMinutesLeadingZero"),l=this._get(e,"onMinuteShow"),d=this._get(e,"minuteText");for(u.starts||(u.starts=0),u.ends||(u.ends=59),u.manual||(u.manual=[]),t=u.starts;t<=u.ends;t+=u.interval)a.push(t);for(i=0;i<u.manual.length;i++){var h=u.manual[i];"number"!=typeof h||0>h||h>59||$.inArray(h,a)>=0||a.push(h)}if(a.sort(function(e,t){return e-t}),o=Math.round(a.length/r+.49),l&&0==l.apply(e.input?e.input[0]:null,[e.hours,e.minutes]))for(c=0;c<a.length;c+=1)if(t=a[c],l.apply(e.input?e.input[0]:null,[e.hours,t])){e.minutes=t;break}for(n+='<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">'+d+'</div><table class="ui-timepicker">',c=0,s=1;r>=s;s++){for(n+="<tr>";s*o>c;){var t=a[c],m="";void 0!==t&&(m=10>t&&p?"0"+t.toString():t.toString()),n+=this._generateHTMLMinuteCell(e,t,m),c++}n+="</tr>"}return n+="</table>"},_generateHTMLHourCell:function(e,t,i,s){var n=t;t>12&&i&&(n=t-12),0==n&&i&&(n=12),10>n&&s&&(n="0"+n);var r="",a=!0,u=this._get(e,"onHourShow"),o=this._get(e,"maxTime"),c=this._get(e,"minTime");return void 0==t?r='<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>':(u&&(a=u.apply(e.input?e.input[0]:null,[t])),a&&(!isNaN(parseInt(o.hour))&&t>o.hour&&(a=!1),!isNaN(parseInt(c.hour))&&t<c.hour&&(a=!1)),r=a?'<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#'+e.id.replace(/\\\\/g,"\\")+'" data-hour="'+t.toString()+'"><a class="ui-state-default '+(t==e.hours?"ui-state-active":"")+'">'+n.toString()+"</a></td>":'<td><span class="ui-state-default ui-state-disabled '+(t==e.hours?" ui-state-active ":" ")+'">'+n.toString()+"</span></td>")},_generateHTMLMinuteCell:function(e,t,i){var s="",n=!0,r=e.hours,a=this._get(e,"onMinuteShow"),u=this._get(e,"maxTime"),o=this._get(e,"minTime");return a&&(n=a.apply(e.input?e.input[0]:null,[e.hours,t])),void 0==t?s='<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>':(n&&null!==r&&(!isNaN(parseInt(u.hour))&&!isNaN(parseInt(u.minute))&&r>=u.hour&&t>u.minute&&(n=!1),!isNaN(parseInt(o.hour))&&!isNaN(parseInt(o.minute))&&r<=o.hour&&t<o.minute&&(n=!1)),s=n?'<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#'+e.id.replace(/\\\\/g,"\\")+'" data-minute="'+t.toString()+'" ><a class="ui-state-default '+(t==e.minutes?"ui-state-active":"")+'" >'+i+"</a></td>":'<td><span class="ui-state-default ui-state-disabled" >'+i+"</span></td>")},_destroyTimepicker:function(e){var t=$(e),i=$.data(e,PROP_NAME);if(t.hasClass(this.markerClassName)){var s=e.nodeName.toLowerCase();$.removeData(e,PROP_NAME),"input"==s?(i.append.remove(),i.trigger.remove(),t.removeClass(this.markerClassName).unbind("focus.timepicker",this._showTimepicker).unbind("click.timepicker",this._adjustZIndex)):("div"==s||"span"==s)&&t.removeClass(this.markerClassName).empty()}},_enableTimepicker:function(e){var t=$(e),i=t.attr("id"),s=$.data(e,PROP_NAME);if(t.hasClass(this.markerClassName)){var n=e.nodeName.toLowerCase();if("input"==n){e.disabled=!1;var r=this._get(s,"button");$(r).removeClass("ui-state-disabled").disabled=!1,s.trigger.filter("button").each(function(){this.disabled=!1}).end()}else if("div"==n||"span"==n){var a=t.children("."+this._inlineClass);a.children().removeClass("ui-state-disabled"),a.find("button").each(function(){this.disabled=!1})}this._disabledInputs=$.map(this._disabledInputs,function(e){return e==i?null:e})}},_disableTimepicker:function(e){var t=$(e),i=$.data(e,PROP_NAME);if(t.hasClass(this.markerClassName)){var s=e.nodeName.toLowerCase();if("input"==s){var n=this._get(i,"button");$(n).addClass("ui-state-disabled").disabled=!0,e.disabled=!0,i.trigger.filter("button").each(function(){this.disabled=!0}).end()}else if("div"==s||"span"==s){var r=t.children("."+this._inlineClass);r.children().addClass("ui-state-disabled"),r.find("button").each(function(){this.disabled=!0})}this._disabledInputs=$.map(this._disabledInputs,function(t){return t==e?null:t}),this._disabledInputs[this._disabledInputs.length]=t.attr("id")}},_isDisabledTimepicker:function(e){if(!e)return!1;for(var t=0;t<this._disabledInputs.length;t++)if(this._disabledInputs[t]==e)return!0;return!1},_checkOffset:function(e,t,i){var s=e.tpDiv.outerWidth(),n=e.tpDiv.outerHeight(),r=e.input?e.input.outerWidth():0,a=e.input?e.input.outerHeight():0,u=document.documentElement.clientWidth+$(document).scrollLeft(),o=document.documentElement.clientHeight+$(document).scrollTop();return t.left-=this._get(e,"isRTL")?s-r:0,t.left-=i&&t.left==e.input.offset().left?$(document).scrollLeft():0,t.top-=i&&t.top==e.input.offset().top+a?$(document).scrollTop():0,t.left-=Math.min(t.left,t.left+s>u&&u>s?Math.abs(t.left+s-u):0),t.top-=Math.min(t.top,t.top+n>o&&o>n?Math.abs(n+a):0),t},_findPos:function(e){for(var t=this._getInst(e),i=this._get(t,"isRTL");e&&("hidden"==e.type||1!=e.nodeType);)e=e[i?"previousSibling":"nextSibling"];var s=$(e).offset();return[s.left,s.top]},_getBorders:function(e){var t=function(e){return{thin:1,medium:2,thick:3}[e]||e};return[parseFloat(t(e.css("border-left-width"))),parseFloat(t(e.css("border-top-width")))]},_checkExternalClick:function(e){if($.timepicker._curInst){var t=$(e.target);t[0].id==$.timepicker._mainDivId||0!=t.parents("#"+$.timepicker._mainDivId).length||t.hasClass($.timepicker.markerClassName)||t.hasClass($.timepicker._triggerClass)||!$.timepicker._timepickerShowing||$.timepicker._inDialog&&$.blockUI||$.timepicker._hideTimepicker()}},_hideTimepicker:function(e){var t=this._curInst;if(t&&(!e||t==$.data(e,PROP_NAME))&&this._timepickerShowing){var i=this._get(t,"showAnim"),s=this._get(t,"duration"),n=function(){$.timepicker._tidyDialog(t),this._curInst=null};$.effects&&$.effects[i]?t.tpDiv.hide(i,$.timepicker._get(t,"showOptions"),s,n):t.tpDiv["slideDown"==i?"slideUp":"fadeIn"==i?"fadeOut":"hide"](i?s:null,n),i||n(),this._timepickerShowing=!1,this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.tpDiv))),this._inDialog=!1;var r=this._get(t,"onClose");r&&r.apply(t.input?t.input[0]:null,[t.input?t.input.val():"",t])}},_tidyDialog:function(e){e.tpDiv.removeClass(this._dialogClass).unbind(".ui-timepicker")},_getInst:function(e){try{return $.data(e,PROP_NAME)}catch(t){throw"Missing instance data for this timepicker"}},_get:function(e,t){return void 0!==e.settings[t]?e.settings[t]:this._defaults[t]},_setTimeFromField:function(e){if(e.input.val()!=e.lastVal){var t=this._get(e,"defaultTime"),i="now"==t?this._getCurrentTimeRounded(e):t;if(0==e.inline&&""!=e.input.val()&&(i=e.input.val()),i instanceof Date)e.hours=i.getHours(),e.minutes=i.getMinutes();else{var s=e.lastVal=i;if(""==i)e.hours=-1,e.minutes=-1;else{var n=this.parseTime(e,s);e.hours=n.hours,e.minutes=n.minutes}}$.timepicker._updateTimepicker(e)}},_optionTimepicker:function(e,t,i){var s=this._getInst(e);if(2==arguments.length&&"string"==typeof t)return"defaults"==t?$.extend({},$.timepicker._defaults):s?"all"==t?$.extend({},s.settings):this._get(s,t):null;var n=t||{};"string"==typeof t&&(n={},n[t]=i),s&&(extendRemove(s.settings,n),this._curInst==s&&(this._hideTimepicker(),this._updateTimepicker(s)),s.inline&&this._updateTimepicker(s))},_setTimeTimepicker:function(e,t){var i=this._getInst(e);i&&(this._setTime(i,t),this._updateTimepicker(i),this._updateAlternate(i,t))},_setTime:function(e,t,i){var s=e.hours,n=e.minutes;if(t instanceof Date)e.hours=t.getHours(),e.minutes=t.getMinutes();else{var t=this.parseTime(e,t);e.hours=t.hours,e.minutes=t.minutes}s==e.hours&&n==e.minutes||i||e.input.trigger("change"),this._updateTimepicker(e),this._updateSelectedValue(e)},_getCurrentTimeRounded:function(e){var t=new Date,i=t.getMinutes(),s=this._get(e,"minutes"),n=Math.round(i/s.interval)*s.interval;return t.setMinutes(n),t},parseTime:function(e,t){var i=new Object;if(i.hours=-1,i.minutes=-1,!t)return"";var s=this._get(e,"timeSeparator"),n=this._get(e,"amPmText"),r=this._get(e,"showHours"),a=this._get(e,"showMinutes"),u=this._get(e,"optionalMinutes"),o=1==this._get(e,"showPeriod"),c=t.indexOf(s);if(-1!=c?(i.hours=parseInt(t.substr(0,c),10),i.minutes=parseInt(t.substr(c+1),10)):!r||a&&!u?!r&&a&&(i.minutes=parseInt(t,10)):i.hours=parseInt(t,10),r){var p=t.toUpperCase();i.hours<12&&o&&-1!=p.indexOf(n[1].toUpperCase())&&(i.hours+=12),12==i.hours&&o&&-1!=p.indexOf(n[0].toUpperCase())&&(i.hours=0)}return i},selectNow:function(e){var t=$(e.target).attr("data-timepicker-instance-id"),i=$(t),s=this._getInst(i[0]),n=new Date;s.hours=n.getHours(),s.minutes=n.getMinutes(),this._updateSelectedValue(s),this._updateTimepicker(s),this._hideTimepicker()},deselectTime:function(e){var t=$(e.target).attr("data-timepicker-instance-id"),i=$(t),s=this._getInst(i[0]);s.hours=-1,s.minutes=-1,this._updateSelectedValue(s),this._hideTimepicker()},selectHours:function(e){var t=$(e.currentTarget),i=t.attr("data-timepicker-instance-id"),s=parseInt(t.attr("data-hour")),n=e.data.fromDoubleClick,r=$(i),a=this._getInst(r[0]),u=1==this._get(a,"showMinutes");if($.timepicker._isDisabledTimepicker(r.attr("id")))return!1;t.parents(".ui-timepicker-hours:first").find("a").removeClass("ui-state-active"),t.children("a").addClass("ui-state-active"),a.hours=s;var o=this._get(a,"onMinuteShow"),c=this._get(a,"maxTime"),p=this._get(a,"minTime");return(o||c.minute||p.minute)&&this._updateMinuteDisplay(a),this._updateSelectedValue(a),a._hoursClicked=!0,(a._minutesClicked||n||0==u)&&$.timepicker._hideTimepicker(),!1},selectMinutes:function(e){var t=$(e.currentTarget),i=t.attr("data-timepicker-instance-id"),s=parseInt(t.attr("data-minute")),n=e.data.fromDoubleClick,r=$(i),a=this._getInst(r[0]),u=1==this._get(a,"showHours");return $.timepicker._isDisabledTimepicker(r.attr("id"))?!1:(t.parents(".ui-timepicker-minutes:first").find("a").removeClass("ui-state-active"),t.children("a").addClass("ui-state-active"),a.minutes=s,this._updateSelectedValue(a),a._minutesClicked=!0,a._hoursClicked||n||0==u?($.timepicker._hideTimepicker(),!1):!1)},_updateSelectedValue:function(e){var t=this._getParsedTime(e);e.input&&(e.input.val(t),e.input.trigger("change"));var i=this._get(e,"onSelect");return i&&i.apply(e.input?e.input[0]:null,[t,e]),this._updateAlternate(e,t),t},_getParsedTime:function(e){if(-1==e.hours&&-1==e.minutes)return"";(e.hours<e.hours.starts||e.hours>e.hours.ends)&&(e.hours=0),(e.minutes<e.minutes.starts||e.minutes>e.minutes.ends)&&(e.minutes=0);var t="",i=1==this._get(e,"showPeriod"),s=1==this._get(e,"showLeadingZero"),n=1==this._get(e,"showHours"),r=1==this._get(e,"showMinutes"),a=1==this._get(e,"optionalMinutes"),u=this._get(e,"amPmText"),o=e.hours?e.hours:0,c=e.minutes?e.minutes:0,p=o?o:0,l="";-1==p&&(p=0),-1==c&&(c=0),i&&(0==e.hours&&(p=12),e.hours<12?t=u[0]:(t=u[1],p>12&&(p-=12)));var d=p.toString();s&&10>p&&(d="0"+d);var h=c.toString();return 10>c&&(h="0"+h),n&&(l+=d),!n||!r||a&&0==h||(l+=this._get(e,"timeSeparator")),!r||a&&0==h||(l+=h),n&&t.length>0&&(l+=this._get(e,"periodSeparator")+t),l},_updateAlternate:function(e,t){var i=this._get(e,"altField");i&&$(i).each(function(e,i){$(i).val(t)})},_getTimeAsDateTimepicker:function(e){var t=this._getInst(e);return-1==t.hours&&-1==t.minutes?"":((t.hours<t.hours.starts||t.hours>t.hours.ends)&&(t.hours=0),(t.minutes<t.minutes.starts||t.minutes>t.minutes.ends)&&(t.minutes=0),new Date(0,0,0,t.hours,t.minutes,0))},_getTimeTimepicker:function(e){var t=this._getInst(e);return this._getParsedTime(t)},_getHourTimepicker:function(e){var t=this._getInst(e);return void 0==t?-1:t.hours},_getMinuteTimepicker:function(e){var t=this._getInst(e);return void 0==t?-1:t.minutes}}),$.fn.timepicker=function(e){$.timepicker.initialized||($(document).mousedown($.timepicker._checkExternalClick),$.timepicker.initialized=!0),0===$("#"+$.timepicker._mainDivId).length&&$("body").append($.timepicker.tpDiv);var t=Array.prototype.slice.call(arguments,1);return"string"!=typeof e||"getTime"!=e&&"getTimeAsDate"!=e&&"getHour"!=e&&"getMinute"!=e?"option"==e&&2==arguments.length&&"string"==typeof arguments[1]?$.timepicker["_"+e+"Timepicker"].apply($.timepicker,[this[0]].concat(t)):this.each(function(){"string"==typeof e?$.timepicker["_"+e+"Timepicker"].apply($.timepicker,[this].concat(t)):$.timepicker._attachTimepicker(this,e)}):$.timepicker["_"+e+"Timepicker"].apply($.timepicker,[this[0]].concat(t))},$.timepicker=new Timepicker,$.timepicker.initialized=!1,$.timepicker.uuid=(new Date).getTime(),$.timepicker.version="0.3.3",window["TP_jQuery_"+tpuuid]=$}(jQuery);
media/js/lib/spectrum.js ADDED
@@ -0,0 +1,2323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Spectrum Colorpicker v1.8.0
2
+ // https://github.com/bgrins/spectrum
3
+ // Author: Brian Grinstead
4
+ // License: MIT
5
+
6
+ (function (factory) {
7
+ "use strict";
8
+
9
+ if (typeof define === 'function' && define.amd) { // AMD
10
+ define(['jquery'], factory);
11
+ }
12
+ else if (typeof exports == "object" && typeof module == "object") { // CommonJS
13
+ module.exports = factory(require('jquery'));
14
+ }
15
+ else { // Browser
16
+ factory(jQuery);
17
+ }
18
+ })(function($, undefined) {
19
+ "use strict";
20
+
21
+ var defaultOpts = {
22
+
23
+ // Callbacks
24
+ beforeShow: noop,
25
+ move: noop,
26
+ change: noop,
27
+ show: noop,
28
+ hide: noop,
29
+
30
+ // Options
31
+ color: false,
32
+ flat: false,
33
+ showInput: false,
34
+ allowEmpty: false,
35
+ showButtons: true,
36
+ clickoutFiresChange: true,
37
+ showInitial: false,
38
+ showPalette: false,
39
+ showPaletteOnly: false,
40
+ hideAfterPaletteSelect: false,
41
+ togglePaletteOnly: false,
42
+ showSelectionPalette: true,
43
+ localStorageKey: false,
44
+ appendTo: "body",
45
+ maxSelectionSize: 7,
46
+ cancelText: "cancel",
47
+ chooseText: "choose",
48
+ togglePaletteMoreText: "more",
49
+ togglePaletteLessText: "less",
50
+ clearText: "Clear Color Selection",
51
+ noColorSelectedText: "No Color Selected",
52
+ preferredFormat: false,
53
+ className: "", // Deprecated - use containerClassName and replacerClassName instead.
54
+ containerClassName: "",
55
+ replacerClassName: "",
56
+ showAlpha: false,
57
+ theme: "sp-light",
58
+ palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]],
59
+ selectionPalette: [],
60
+ disabled: false,
61
+ offset: null
62
+ },
63
+ spectrums = [],
64
+ IE = !!/msie/i.exec( window.navigator.userAgent ),
65
+ rgbaSupport = (function() {
66
+ function contains( str, substr ) {
67
+ return !!~('' + str).indexOf(substr);
68
+ }
69
+
70
+ var elem = document.createElement('div');
71
+ var style = elem.style;
72
+ style.cssText = 'background-color:rgba(0,0,0,.5)';
73
+ return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla');
74
+ })(),
75
+ replaceInput = [
76
+ "<div class='sp-replacer'>",
77
+ "<div class='sp-preview'><div class='sp-preview-inner'></div></div>",
78
+ "<div class='sp-dd'>&#9660;</div>",
79
+ "</div>"
80
+ ].join(''),
81
+ markup = (function () {
82
+
83
+ // IE does not support gradients with multiple stops, so we need to simulate
84
+ // that for the rainbow slider with 8 divs that each have a single gradient
85
+ var gradientFix = "";
86
+ if (IE) {
87
+ for (var i = 1; i <= 6; i++) {
88
+ gradientFix += "<div class='sp-" + i + "'></div>";
89
+ }
90
+ }
91
+
92
+ return [
93
+ "<div class='sp-container sp-hidden'>",
94
+ "<div class='sp-palette-container'>",
95
+ "<div class='sp-palette sp-thumb sp-cf'></div>",
96
+ "<div class='sp-palette-button-container sp-cf'>",
97
+ "<button type='button' class='sp-palette-toggle'></button>",
98
+ "</div>",
99
+ "</div>",
100
+ "<div class='sp-picker-container'>",
101
+ "<div class='sp-top sp-cf'>",
102
+ "<div class='sp-fill'></div>",
103
+ "<div class='sp-top-inner'>",
104
+ "<div class='sp-color'>",
105
+ "<div class='sp-sat'>",
106
+ "<div class='sp-val'>",
107
+ "<div class='sp-dragger'></div>",
108
+ "</div>",
109
+ "</div>",
110
+ "</div>",
111
+ "<div class='sp-clear sp-clear-display'>",
112
+ "</div>",
113
+ "<div class='sp-hue'>",
114
+ "<div class='sp-slider'></div>",
115
+ gradientFix,
116
+ "</div>",
117
+ "</div>",
118
+ "<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>",
119
+ "</div>",
120
+ "<div class='sp-input-container sp-cf'>",
121
+ "<input class='sp-input' type='text' spellcheck='false' />",
122
+ "</div>",
123
+ "<div class='sp-initial sp-thumb sp-cf'></div>",
124
+ "<div class='sp-button-container sp-cf'>",
125
+ "<a class='sp-cancel' href='#'></a>",
126
+ "<button type='button' class='sp-choose'></button>",
127
+ "</div>",
128
+ "</div>",
129
+ "</div>"
130
+ ].join("");
131
+ })();
132
+
133
+ function paletteTemplate (p, color, className, opts) {
134
+ var html = [];
135
+ for (var i = 0; i < p.length; i++) {
136
+ var current = p[i];
137
+ if(current) {
138
+ var tiny = tinycolor(current);
139
+ var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light";
140
+ c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : "";
141
+ var formattedString = tiny.toString(opts.preferredFormat || "rgb");
142
+ var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter();
143
+ html.push('<span title="' + formattedString + '" data-color="' + tiny.toRgbString() + '" class="' + c + '"><span class="sp-thumb-inner" style="' + swatchStyle + ';" /></span>');
144
+ } else {
145
+ var cls = 'sp-clear-display';
146
+ html.push($('<div />')
147
+ .append($('<span data-color="" style="background-color:transparent;" class="' + cls + '"></span>')
148
+ .attr('title', opts.noColorSelectedText)
149
+ )
150
+ .html()
151
+ );
152
+ }
153
+ }
154
+ return "<div class='sp-cf " + className + "'>" + html.join('') + "</div>";
155
+ }
156
+
157
+ function hideAll() {
158
+ for (var i = 0; i < spectrums.length; i++) {
159
+ if (spectrums[i]) {
160
+ spectrums[i].hide();
161
+ }
162
+ }
163
+ }
164
+
165
+ function instanceOptions(o, callbackContext) {
166
+ var opts = $.extend({}, defaultOpts, o);
167
+ opts.callbacks = {
168
+ 'move': bind(opts.move, callbackContext),
169
+ 'change': bind(opts.change, callbackContext),
170
+ 'show': bind(opts.show, callbackContext),
171
+ 'hide': bind(opts.hide, callbackContext),
172
+ 'beforeShow': bind(opts.beforeShow, callbackContext)
173
+ };
174
+
175
+ return opts;
176
+ }
177
+
178
+ function spectrum(element, o) {
179
+
180
+ var opts = instanceOptions(o, element),
181
+ flat = opts.flat,
182
+ showSelectionPalette = opts.showSelectionPalette,
183
+ localStorageKey = opts.localStorageKey,
184
+ theme = opts.theme,
185
+ callbacks = opts.callbacks,
186
+ resize = throttle(reflow, 10),
187
+ visible = false,
188
+ isDragging = false,
189
+ dragWidth = 0,
190
+ dragHeight = 0,
191
+ dragHelperHeight = 0,
192
+ slideHeight = 0,
193
+ slideWidth = 0,
194
+ alphaWidth = 0,
195
+ alphaSlideHelperWidth = 0,
196
+ slideHelperHeight = 0,
197
+ currentHue = 0,
198
+ currentSaturation = 0,
199
+ currentValue = 0,
200
+ currentAlpha = 1,
201
+ palette = [],
202
+ paletteArray = [],
203
+ paletteLookup = {},
204
+ selectionPalette = opts.selectionPalette.slice(0),
205
+ maxSelectionSize = opts.maxSelectionSize,
206
+ draggingClass = "sp-dragging",
207
+ shiftMovementDirection = null;
208
+
209
+ var doc = element.ownerDocument,
210
+ body = doc.body,
211
+ boundElement = $(element),
212
+ disabled = false,
213
+ container = $(markup, doc).addClass(theme),
214
+ pickerContainer = container.find(".sp-picker-container"),
215
+ dragger = container.find(".sp-color"),
216
+ dragHelper = container.find(".sp-dragger"),
217
+ slider = container.find(".sp-hue"),
218
+ slideHelper = container.find(".sp-slider"),
219
+ alphaSliderInner = container.find(".sp-alpha-inner"),
220
+ alphaSlider = container.find(".sp-alpha"),
221
+ alphaSlideHelper = container.find(".sp-alpha-handle"),
222
+ textInput = container.find(".sp-input"),
223
+ paletteContainer = container.find(".sp-palette"),
224
+ initialColorContainer = container.find(".sp-initial"),
225
+ cancelButton = container.find(".sp-cancel"),
226
+ clearButton = container.find(".sp-clear"),
227
+ chooseButton = container.find(".sp-choose"),
228
+ toggleButton = container.find(".sp-palette-toggle"),
229
+ isInput = boundElement.is("input"),
230
+ isInputTypeColor = isInput && boundElement.attr("type") === "color" && inputTypeColorSupport(),
231
+ shouldReplace = isInput && !flat,
232
+ replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]),
233
+ offsetElement = (shouldReplace) ? replacer : boundElement,
234
+ previewElement = replacer.find(".sp-preview-inner"),
235
+ initialColor = opts.color || (isInput && boundElement.val()),
236
+ colorOnShow = false,
237
+ currentPreferredFormat = opts.preferredFormat,
238
+ clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange,
239
+ isEmpty = !initialColor,
240
+ allowEmpty = opts.allowEmpty && !isInputTypeColor;
241
+
242
+ function applyOptions() {
243
+
244
+ if (opts.showPaletteOnly) {
245
+ opts.showPalette = true;
246
+ }
247
+
248
+ toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
249
+
250
+ if (opts.palette) {
251
+ palette = opts.palette.slice(0);
252
+ paletteArray = $.isArray(palette[0]) ? palette : [palette];
253
+ paletteLookup = {};
254
+ for (var i = 0; i < paletteArray.length; i++) {
255
+ for (var j = 0; j < paletteArray[i].length; j++) {
256
+ var rgb = tinycolor(paletteArray[i][j]).toRgbString();
257
+ paletteLookup[rgb] = true;
258
+ }
259
+ }
260
+ }
261
+
262
+ container.toggleClass("sp-flat", flat);
263
+ container.toggleClass("sp-input-disabled", !opts.showInput);
264
+ container.toggleClass("sp-alpha-enabled", opts.showAlpha);
265
+ container.toggleClass("sp-clear-enabled", allowEmpty);
266
+ container.toggleClass("sp-buttons-disabled", !opts.showButtons);
267
+ container.toggleClass("sp-palette-buttons-disabled", !opts.togglePaletteOnly);
268
+ container.toggleClass("sp-palette-disabled", !opts.showPalette);
269
+ container.toggleClass("sp-palette-only", opts.showPaletteOnly);
270
+ container.toggleClass("sp-initial-disabled", !opts.showInitial);
271
+ container.addClass(opts.className).addClass(opts.containerClassName);
272
+
273
+ reflow();
274
+ }
275
+
276
+ function initialize() {
277
+
278
+ if (IE) {
279
+ container.find("*:not(input)").attr("unselectable", "on");
280
+ }
281
+
282
+ applyOptions();
283
+
284
+ if (shouldReplace) {
285
+ boundElement.after(replacer).hide();
286
+ }
287
+
288
+ if (!allowEmpty) {
289
+ clearButton.hide();
290
+ }
291
+
292
+ if (flat) {
293
+ boundElement.after(container).hide();
294
+ }
295
+ else {
296
+
297
+ var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo);
298
+ if (appendTo.length !== 1) {
299
+ appendTo = $("body");
300
+ }
301
+
302
+ appendTo.append(container);
303
+ }
304
+
305
+ updateSelectionPaletteFromStorage();
306
+
307
+ offsetElement.bind("click.spectrum touchstart.spectrum", function (e) {
308
+ if (!disabled) {
309
+ toggle();
310
+ }
311
+
312
+ e.stopPropagation();
313
+
314
+ if (!$(e.target).is("input")) {
315
+ e.preventDefault();
316
+ }
317
+ });
318
+
319
+ if(boundElement.is(":disabled") || (opts.disabled === true)) {
320
+ disable();
321
+ }
322
+
323
+ // Prevent clicks from bubbling up to document. This would cause it to be hidden.
324
+ container.click(stopPropagation);
325
+
326
+ // Handle user typed input
327
+ textInput.change(setFromTextInput);
328
+ textInput.bind("paste", function () {
329
+ setTimeout(setFromTextInput, 1);
330
+ });
331
+ textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } });
332
+
333
+ cancelButton.text(opts.cancelText);
334
+ cancelButton.bind("click.spectrum", function (e) {
335
+ e.stopPropagation();
336
+ e.preventDefault();
337
+ revert();
338
+ hide();
339
+ });
340
+
341
+ clearButton.attr("title", opts.clearText);
342
+ clearButton.bind("click.spectrum", function (e) {
343
+ e.stopPropagation();
344
+ e.preventDefault();
345
+ isEmpty = true;
346
+ move();
347
+
348
+ if(flat) {
349
+ //for the flat style, this is a change event
350
+ updateOriginalInput(true);
351
+ }
352
+ });
353
+
354
+ chooseButton.text(opts.chooseText);
355
+ chooseButton.bind("click.spectrum", function (e) {
356
+ e.stopPropagation();
357
+ e.preventDefault();
358
+
359
+ if (IE && textInput.is(":focus")) {
360
+ textInput.trigger('change');
361
+ }
362
+
363
+ if (isValid()) {
364
+ updateOriginalInput(true);
365
+ hide();
366
+ }
367
+ });
368
+
369
+ toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText);
370
+ toggleButton.bind("click.spectrum", function (e) {
371
+ e.stopPropagation();
372
+ e.preventDefault();
373
+
374
+ opts.showPaletteOnly = !opts.showPaletteOnly;
375
+
376
+ // To make sure the Picker area is drawn on the right, next to the
377
+ // Palette area (and not below the palette), first move the Palette
378
+ // to the left to make space for the picker, plus 5px extra.
379
+ // The 'applyOptions' function puts the whole container back into place
380
+ // and takes care of the button-text and the sp-palette-only CSS class.
381
+ if (!opts.showPaletteOnly && !flat) {
382
+ container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5));
383
+ }
384
+ applyOptions();
385
+ });
386
+
387
+ draggable(alphaSlider, function (dragX, dragY, e) {
388
+ currentAlpha = (dragX / alphaWidth);
389
+ isEmpty = false;
390
+ if (e.shiftKey) {
391
+ currentAlpha = Math.round(currentAlpha * 10) / 10;
392
+ }
393
+
394
+ move();
395
+ }, dragStart, dragStop);
396
+
397
+ draggable(slider, function (dragX, dragY) {
398
+ currentHue = parseFloat(dragY / slideHeight);
399
+ isEmpty = false;
400
+ if (!opts.showAlpha) {
401
+ currentAlpha = 1;
402
+ }
403
+ move();
404
+ }, dragStart, dragStop);
405
+
406
+ draggable(dragger, function (dragX, dragY, e) {
407
+
408
+ // shift+drag should snap the movement to either the x or y axis.
409
+ if (!e.shiftKey) {
410
+ shiftMovementDirection = null;
411
+ }
412
+ else if (!shiftMovementDirection) {
413
+ var oldDragX = currentSaturation * dragWidth;
414
+ var oldDragY = dragHeight - (currentValue * dragHeight);
415
+ var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY);
416
+
417
+ shiftMovementDirection = furtherFromX ? "x" : "y";
418
+ }
419
+
420
+ var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x";
421
+ var setValue = !shiftMovementDirection || shiftMovementDirection === "y";
422
+
423
+ if (setSaturation) {
424
+ currentSaturation = parseFloat(dragX / dragWidth);
425
+ }
426
+ if (setValue) {
427
+ currentValue = parseFloat((dragHeight - dragY) / dragHeight);
428
+ }
429
+
430
+ isEmpty = false;
431
+ if (!opts.showAlpha) {
432
+ currentAlpha = 1;
433
+ }
434
+
435
+ move();
436
+
437
+ }, dragStart, dragStop);
438
+
439
+ if (!!initialColor) {
440
+ set(initialColor);
441
+
442
+ // In case color was black - update the preview UI and set the format
443
+ // since the set function will not run (default color is black).
444
+ updateUI();
445
+ currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format;
446
+
447
+ addColorToSelectionPalette(initialColor);
448
+ }
449
+ else {
450
+ updateUI();
451
+ }
452
+
453
+ if (flat) {
454
+ show();
455
+ }
456
+
457
+ function paletteElementClick(e) {
458
+ if (e.data && e.data.ignore) {
459
+ set($(e.target).closest(".sp-thumb-el").data("color"));
460
+ move();
461
+ }
462
+ else {
463
+ set($(e.target).closest(".sp-thumb-el").data("color"));
464
+ move();
465
+ updateOriginalInput(true);
466
+ if (opts.hideAfterPaletteSelect) {
467
+ hide();
468
+ }
469
+ }
470
+
471
+ return false;
472
+ }
473
+
474
+ var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum";
475
+ paletteContainer.delegate(".sp-thumb-el", paletteEvent, paletteElementClick);
476
+ initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, paletteElementClick);
477
+ }
478
+
479
+ function updateSelectionPaletteFromStorage() {
480
+
481
+ if (localStorageKey && window.localStorage) {
482
+
483
+ // Migrate old palettes over to new format. May want to remove this eventually.
484
+ try {
485
+ var oldPalette = window.localStorage[localStorageKey].split(",#");
486
+ if (oldPalette.length > 1) {
487
+ delete window.localStorage[localStorageKey];
488
+ $.each(oldPalette, function(i, c) {
489
+ addColorToSelectionPalette(c);
490
+ });
491
+ }
492
+ }
493
+ catch(e) { }
494
+
495
+ try {
496
+ selectionPalette = window.localStorage[localStorageKey].split(";");
497
+ }
498
+ catch (e) { }
499
+ }
500
+ }
501
+
502
+ function addColorToSelectionPalette(color) {
503
+ if (showSelectionPalette) {
504
+ var rgb = tinycolor(color).toRgbString();
505
+ if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) {
506
+ selectionPalette.push(rgb);
507
+ while(selectionPalette.length > maxSelectionSize) {
508
+ selectionPalette.shift();
509
+ }
510
+ }
511
+
512
+ if (localStorageKey && window.localStorage) {
513
+ try {
514
+ window.localStorage[localStorageKey] = selectionPalette.join(";");
515
+ }
516
+ catch(e) { }
517
+ }
518
+ }
519
+ }
520
+
521
+ function getUniqueSelectionPalette() {
522
+ var unique = [];
523
+ if (opts.showPalette) {
524
+ for (var i = 0; i < selectionPalette.length; i++) {
525
+ var rgb = tinycolor(selectionPalette[i]).toRgbString();
526
+
527
+ if (!paletteLookup[rgb]) {
528
+ unique.push(selectionPalette[i]);
529
+ }
530
+ }
531
+ }
532
+
533
+ return unique.reverse().slice(0, opts.maxSelectionSize);
534
+ }
535
+
536
+ function drawPalette() {
537
+
538
+ var currentColor = get();
539
+
540
+ var html = $.map(paletteArray, function (palette, i) {
541
+ return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts);
542
+ });
543
+
544
+ updateSelectionPaletteFromStorage();
545
+
546
+ if (selectionPalette) {
547
+ html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts));
548
+ }
549
+
550
+ paletteContainer.html(html.join(""));
551
+ }
552
+
553
+ function drawInitial() {
554
+ if (opts.showInitial) {
555
+ var initial = colorOnShow;
556
+ var current = get();
557
+ initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts));
558
+ }
559
+ }
560
+
561
+ function dragStart() {
562
+ if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) {
563
+ reflow();
564
+ }
565
+ isDragging = true;
566
+ container.addClass(draggingClass);
567
+ shiftMovementDirection = null;
568
+ boundElement.trigger('dragstart.spectrum', [ get() ]);
569
+ }
570
+
571
+ function dragStop() {
572
+ isDragging = false;
573
+ container.removeClass(draggingClass);
574
+ boundElement.trigger('dragstop.spectrum', [ get() ]);
575
+ }
576
+
577
+ function setFromTextInput() {
578
+
579
+ var value = textInput.val();
580
+
581
+ if ((value === null || value === "") && allowEmpty) {
582
+ set(null);
583
+ updateOriginalInput(true);
584
+ }
585
+ else {
586
+ var tiny = tinycolor(value);
587
+ if (tiny.isValid()) {
588
+ set(tiny);
589
+ updateOriginalInput(true);
590
+ }
591
+ else {
592
+ textInput.addClass("sp-validation-error");
593
+ }
594
+ }
595
+ }
596
+
597
+ function toggle() {
598
+ if (visible) {
599
+ hide();
600
+ }
601
+ else {
602
+ show();
603
+ }
604
+ }
605
+
606
+ function show() {
607
+ var event = $.Event('beforeShow.spectrum');
608
+
609
+ if (visible) {
610
+ reflow();
611
+ return;
612
+ }
613
+
614
+ boundElement.trigger(event, [ get() ]);
615
+
616
+ if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) {
617
+ return;
618
+ }
619
+
620
+ hideAll();
621
+ visible = true;
622
+
623
+ $(doc).bind("keydown.spectrum", onkeydown);
624
+ $(doc).bind("click.spectrum", clickout);
625
+ $(window).bind("resize.spectrum", resize);
626
+ replacer.addClass("sp-active");
627
+ container.removeClass("sp-hidden");
628
+
629
+ reflow();
630
+ updateUI();
631
+
632
+ colorOnShow = get();
633
+
634
+ drawInitial();
635
+ callbacks.show(colorOnShow);
636
+ boundElement.trigger('show.spectrum', [ colorOnShow ]);
637
+ }
638
+
639
+ function onkeydown(e) {
640
+ // Close on ESC
641
+ if (e.keyCode === 27) {
642
+ hide();
643
+ }
644
+ }
645
+
646
+ function clickout(e) {
647
+ // Return on right click.
648
+ if (e.button == 2) { return; }
649
+
650
+ // If a drag event was happening during the mouseup, don't hide
651
+ // on click.
652
+ if (isDragging) { return; }
653
+
654
+ if (clickoutFiresChange) {
655
+ updateOriginalInput(true);
656
+ }
657
+ else {
658
+ revert();
659
+ }
660
+ hide();
661
+ }
662
+
663
+ function hide() {
664
+ // Return if hiding is unnecessary
665
+ if (!visible || flat) { return; }
666
+ visible = false;
667
+
668
+ $(doc).unbind("keydown.spectrum", onkeydown);
669
+ $(doc).unbind("click.spectrum", clickout);
670
+ $(window).unbind("resize.spectrum", resize);
671
+
672
+ replacer.removeClass("sp-active");
673
+ container.addClass("sp-hidden");
674
+
675
+ callbacks.hide(get());
676
+ boundElement.trigger('hide.spectrum', [ get() ]);
677
+ }
678
+
679
+ function revert() {
680
+ set(colorOnShow, true);
681
+ }
682
+
683
+ function set(color, ignoreFormatChange) {
684
+ if (tinycolor.equals(color, get())) {
685
+ // Update UI just in case a validation error needs
686
+ // to be cleared.
687
+ updateUI();
688
+ return;
689
+ }
690
+
691
+ var newColor, newHsv;
692
+ if (!color && allowEmpty) {
693
+ isEmpty = true;
694
+ } else {
695
+ isEmpty = false;
696
+ newColor = tinycolor(color);
697
+ newHsv = newColor.toHsv();
698
+
699
+ currentHue = (newHsv.h % 360) / 360;
700
+ currentSaturation = newHsv.s;
701
+ currentValue = newHsv.v;
702
+ currentAlpha = newHsv.a;
703
+ }
704
+ updateUI();
705
+
706
+ if (newColor && newColor.isValid() && !ignoreFormatChange) {
707
+ currentPreferredFormat = opts.preferredFormat || newColor.getFormat();
708
+ }
709
+ }
710
+
711
+ function get(opts) {
712
+ opts = opts || { };
713
+
714
+ if (allowEmpty && isEmpty) {
715
+ return null;
716
+ }
717
+
718
+ return tinycolor.fromRatio({
719
+ h: currentHue,
720
+ s: currentSaturation,
721
+ v: currentValue,
722
+ a: Math.round(currentAlpha * 100) / 100
723
+ }, { format: opts.format || currentPreferredFormat });
724
+ }
725
+
726
+ function isValid() {
727
+ return !textInput.hasClass("sp-validation-error");
728
+ }
729
+
730
+ function move() {
731
+ updateUI();
732
+
733
+ callbacks.move(get());
734
+ boundElement.trigger('move.spectrum', [ get() ]);
735
+ }
736
+
737
+ function updateUI() {
738
+
739
+ textInput.removeClass("sp-validation-error");
740
+
741
+ updateHelperLocations();
742
+
743
+ // Update dragger background color (gradients take care of saturation and value).
744
+ var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 });
745
+ dragger.css("background-color", flatColor.toHexString());
746
+
747
+ // Get a format that alpha will be included in (hex and names ignore alpha)
748
+ var format = currentPreferredFormat;
749
+ if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) {
750
+ if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") {
751
+ format = "rgb";
752
+ }
753
+ }
754
+
755
+ var realColor = get({ format: format }),
756
+ displayColor = '';
757
+
758
+ //reset background info for preview element
759
+ previewElement.removeClass("sp-clear-display");
760
+ previewElement.css('background-color', 'transparent');
761
+
762
+ if (!realColor && allowEmpty) {
763
+ // Update the replaced elements background with icon indicating no color selection
764
+ previewElement.addClass("sp-clear-display");
765
+ }
766
+ else {
767
+ var realHex = realColor.toHexString(),
768
+ realRgb = realColor.toRgbString();
769
+
770
+ // Update the replaced elements background color (with actual selected color)
771
+ if (rgbaSupport || realColor.alpha === 1) {
772
+ previewElement.css("background-color", realRgb);
773
+ }
774
+ else {
775
+ previewElement.css("background-color", "transparent");
776
+ previewElement.css("filter", realColor.toFilter());
777
+ }
778
+
779
+ if (opts.showAlpha) {
780
+ var rgb = realColor.toRgb();
781
+ rgb.a = 0;
782
+ var realAlpha = tinycolor(rgb).toRgbString();
783
+ var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")";
784
+
785
+ if (IE) {
786
+ alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex));
787
+ }
788
+ else {
789
+ alphaSliderInner.css("background", "-webkit-" + gradient);
790
+ alphaSliderInner.css("background", "-moz-" + gradient);
791
+ alphaSliderInner.css("background", "-ms-" + gradient);
792
+ // Use current syntax gradient on unprefixed property.
793
+ alphaSliderInner.css("background",
794
+ "linear-gradient(to right, " + realAlpha + ", " + realHex + ")");
795
+ }
796
+ }
797
+
798
+ displayColor = realColor.toString(format);
799
+ }
800
+
801
+ // Update the text entry input as it changes happen
802
+ if (opts.showInput) {
803
+ textInput.val(displayColor);
804
+ }
805
+
806
+ if (opts.showPalette) {
807
+ drawPalette();
808
+ }
809
+
810
+ drawInitial();
811
+ }
812
+
813
+ function updateHelperLocations() {
814
+ var s = currentSaturation;
815
+ var v = currentValue;
816
+
817
+ if(allowEmpty && isEmpty) {
818
+ //if selected color is empty, hide the helpers
819
+ alphaSlideHelper.hide();
820
+ slideHelper.hide();
821
+ dragHelper.hide();
822
+ }
823
+ else {
824
+ //make sure helpers are visible
825
+ alphaSlideHelper.show();
826
+ slideHelper.show();
827
+ dragHelper.show();
828
+
829
+ // Where to show the little circle in that displays your current selected color
830
+ var dragX = s * dragWidth;
831
+ var dragY = dragHeight - (v * dragHeight);
832
+ dragX = Math.max(
833
+ -dragHelperHeight,
834
+ Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight)
835
+ );
836
+ dragY = Math.max(
837
+ -dragHelperHeight,
838
+ Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight)
839
+ );
840
+ dragHelper.css({
841
+ "top": dragY + "px",
842
+ "left": dragX + "px"
843
+ });
844
+
845
+ var alphaX = currentAlpha * alphaWidth;
846
+ alphaSlideHelper.css({
847
+ "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px"
848
+ });
849
+
850
+ // Where to show the bar that displays your current selected hue
851
+ var slideY = (currentHue) * slideHeight;
852
+ slideHelper.css({
853
+ "top": (slideY - slideHelperHeight) + "px"
854
+ });
855
+ }
856
+ }
857
+
858
+ function updateOriginalInput(fireCallback) {
859
+ var color = get(),
860
+ displayColor = '',
861
+ hasChanged = !tinycolor.equals(color, colorOnShow);
862
+
863
+ if (color) {
864
+ displayColor = color.toString(currentPreferredFormat);
865
+ // Update the selection palette with the current color
866
+ addColorToSelectionPalette(color);
867
+ }
868
+
869
+ if (isInput) {
870
+ boundElement.val(displayColor);
871
+ }
872
+
873
+ if (fireCallback && hasChanged) {
874
+ callbacks.change(color);
875
+ boundElement.trigger('change', [ color ]);
876
+ }
877
+ }
878
+
879
+ function reflow() {
880
+ if (!visible) {
881
+ return; // Calculations would be useless and wouldn't be reliable anyways
882
+ }
883
+ dragWidth = dragger.width();
884
+ dragHeight = dragger.height();
885
+ dragHelperHeight = dragHelper.height();
886
+ slideWidth = slider.width();
887
+ slideHeight = slider.height();
888
+ slideHelperHeight = slideHelper.height();
889
+ alphaWidth = alphaSlider.width();
890
+ alphaSlideHelperWidth = alphaSlideHelper.width();
891
+
892
+ if (!flat) {
893
+ container.css("position", "absolute");
894
+ if (opts.offset) {
895
+ container.offset(opts.offset);
896
+ } else {
897
+ container.offset(getOffset(container, offsetElement));
898
+ }
899
+ }
900
+
901
+ updateHelperLocations();
902
+
903
+ if (opts.showPalette) {
904
+ drawPalette();
905
+ }
906
+
907
+ boundElement.trigger('reflow.spectrum');
908
+ }
909
+
910
+ function destroy() {
911
+ boundElement.show();
912
+ offsetElement.unbind("click.spectrum touchstart.spectrum");
913
+ container.remove();
914
+ replacer.remove();
915
+ spectrums[spect.id] = null;
916
+ }
917
+
918
+ function option(optionName, optionValue) {
919
+ if (optionName === undefined) {
920
+ return $.extend({}, opts);
921
+ }
922
+ if (optionValue === undefined) {
923
+ return opts[optionName];
924
+ }
925
+
926
+ opts[optionName] = optionValue;
927
+
928
+ if (optionName === "preferredFormat") {
929
+ currentPreferredFormat = opts.preferredFormat;
930
+ }
931
+ applyOptions();
932
+ }
933
+
934
+ function enable() {
935
+ disabled = false;
936
+ boundElement.attr("disabled", false);
937
+ offsetElement.removeClass("sp-disabled");
938
+ }
939
+
940
+ function disable() {
941
+ hide();
942
+ disabled = true;
943
+ boundElement.attr("disabled", true);
944
+ offsetElement.addClass("sp-disabled");
945
+ }
946
+
947
+ function setOffset(coord) {
948
+ opts.offset = coord;
949
+ reflow();
950
+ }
951
+
952
+ initialize();
953
+
954
+ var spect = {
955
+ show: show,
956
+ hide: hide,
957
+ toggle: toggle,
958
+ reflow: reflow,
959
+ option: option,
960
+ enable: enable,
961
+ disable: disable,
962
+ offset: setOffset,
963
+ set: function (c) {
964
+ set(c);
965
+ updateOriginalInput();
966
+ },
967
+ get: get,
968
+ destroy: destroy,
969
+ container: container
970
+ };
971
+
972
+ spect.id = spectrums.push(spect) - 1;
973
+
974
+ return spect;
975
+ }
976
+
977
+ /**
978
+ * checkOffset - get the offset below/above and left/right element depending on screen position
979
+ * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js
980
+ */
981
+ function getOffset(picker, input) {
982
+ var extraY = 0;
983
+ var dpWidth = picker.outerWidth();
984
+ var dpHeight = picker.outerHeight();
985
+ var inputHeight = input.outerHeight();
986
+ var doc = picker[0].ownerDocument;
987
+ var docElem = doc.documentElement;
988
+ var viewWidth = docElem.clientWidth + $(doc).scrollLeft();
989
+ var viewHeight = docElem.clientHeight + $(doc).scrollTop();
990
+ var offset = input.offset();
991
+ offset.top += inputHeight;
992
+
993
+ offset.left -=
994
+ Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
995
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
996
+
997
+ offset.top -=
998
+ Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
999
+ Math.abs(dpHeight + inputHeight - extraY) : extraY));
1000
+
1001
+ return offset;
1002
+ }
1003
+
1004
+ /**
1005
+ * noop - do nothing
1006
+ */
1007
+ function noop() {
1008
+
1009
+ }
1010
+
1011
+ /**
1012
+ * stopPropagation - makes the code only doing this a little easier to read in line
1013
+ */
1014
+ function stopPropagation(e) {
1015
+ e.stopPropagation();
1016
+ }
1017
+
1018
+ /**
1019
+ * Create a function bound to a given object
1020
+ * Thanks to underscore.js
1021
+ */
1022
+ function bind(func, obj) {
1023
+ var slice = Array.prototype.slice;
1024
+ var args = slice.call(arguments, 2);
1025
+ return function () {
1026
+ return func.apply(obj, args.concat(slice.call(arguments)));
1027
+ };
1028
+ }
1029
+
1030
+ /**
1031
+ * Lightweight drag helper. Handles containment within the element, so that
1032
+ * when dragging, the x is within [0,element.width] and y is within [0,element.height]
1033
+ */
1034
+ function draggable(element, onmove, onstart, onstop) {
1035
+ onmove = onmove || function () { };
1036
+ onstart = onstart || function () { };
1037
+ onstop = onstop || function () { };
1038
+ var doc = document;
1039
+ var dragging = false;
1040
+ var offset = {};
1041
+ var maxHeight = 0;
1042
+ var maxWidth = 0;
1043
+ var hasTouch = ('ontouchstart' in window);
1044
+
1045
+ var duringDragEvents = {};
1046
+ duringDragEvents["selectstart"] = prevent;
1047
+ duringDragEvents["dragstart"] = prevent;
1048
+ duringDragEvents["touchmove mousemove"] = move;
1049
+ duringDragEvents["touchend mouseup"] = stop;
1050
+
1051
+ function prevent(e) {
1052
+ if (e.stopPropagation) {
1053
+ e.stopPropagation();
1054
+ }
1055
+ if (e.preventDefault) {
1056
+ e.preventDefault();
1057
+ }
1058
+ e.returnValue = false;
1059
+ }
1060
+
1061
+ function move(e) {
1062
+ if (dragging) {
1063
+ // Mouseup happened outside of window
1064
+ if (IE && doc.documentMode < 9 && !e.button) {
1065
+ return stop();
1066
+ }
1067
+
1068
+ var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0];
1069
+ var pageX = t0 && t0.pageX || e.pageX;
1070
+ var pageY = t0 && t0.pageY || e.pageY;
1071
+
1072
+ var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
1073
+ var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
1074
+
1075
+ if (hasTouch) {
1076
+ // Stop scrolling in iOS
1077
+ prevent(e);
1078
+ }
1079
+
1080
+ onmove.apply(element, [dragX, dragY, e]);
1081
+ }
1082
+ }
1083
+
1084
+ function start(e) {
1085
+ var rightclick = (e.which) ? (e.which == 3) : (e.button == 2);
1086
+
1087
+ if (!rightclick && !dragging) {
1088
+ if (onstart.apply(element, arguments) !== false) {
1089
+ dragging = true;
1090
+ maxHeight = $(element).height();
1091
+ maxWidth = $(element).width();
1092
+ offset = $(element).offset();
1093
+
1094
+ $(doc).bind(duringDragEvents);
1095
+ $(doc.body).addClass("sp-dragging");
1096
+
1097
+ move(e);
1098
+
1099
+ prevent(e);
1100
+ }
1101
+ }
1102
+ }
1103
+
1104
+ function stop() {
1105
+ if (dragging) {
1106
+ $(doc).unbind(duringDragEvents);
1107
+ $(doc.body).removeClass("sp-dragging");
1108
+
1109
+ // Wait a tick before notifying observers to allow the click event
1110
+ // to fire in Chrome.
1111
+ setTimeout(function() {
1112
+ onstop.apply(element, arguments);
1113
+ }, 0);
1114
+ }
1115
+ dragging = false;
1116
+ }
1117
+
1118
+ $(element).bind("touchstart mousedown", start);
1119
+ }
1120
+
1121
+ function throttle(func, wait, debounce) {
1122
+ var timeout;
1123
+ return function () {
1124
+ var context = this, args = arguments;
1125
+ var throttler = function () {
1126
+ timeout = null;
1127
+ func.apply(context, args);
1128
+ };
1129
+ if (debounce) clearTimeout(timeout);
1130
+ if (debounce || !timeout) timeout = setTimeout(throttler, wait);
1131
+ };
1132
+ }
1133
+
1134
+ function inputTypeColorSupport() {
1135
+ return $.fn.spectrum.inputTypeColorSupport();
1136
+ }
1137
+
1138
+ /**
1139
+ * Define a jQuery plugin
1140
+ */
1141
+ var dataID = "spectrum.id";
1142
+ $.fn.spectrum = function (opts, extra) {
1143
+
1144
+ if (typeof opts == "string") {
1145
+
1146
+ var returnValue = this;
1147
+ var args = Array.prototype.slice.call( arguments, 1 );
1148
+
1149
+ this.each(function () {
1150
+ var spect = spectrums[$(this).data(dataID)];
1151
+ if (spect) {
1152
+ var method = spect[opts];
1153
+ if (!method) {
1154
+ throw new Error( "Spectrum: no such method: '" + opts + "'" );
1155
+ }
1156
+
1157
+ if (opts == "get") {
1158
+ returnValue = spect.get();
1159
+ }
1160
+ else if (opts == "container") {
1161
+ returnValue = spect.container;
1162
+ }
1163
+ else if (opts == "option") {
1164
+ returnValue = spect.option.apply(spect, args);
1165
+ }
1166
+ else if (opts == "destroy") {
1167
+ spect.destroy();
1168
+ $(this).removeData(dataID);
1169
+ }
1170
+ else {
1171
+ method.apply(spect, args);
1172
+ }
1173
+ }
1174
+ });
1175
+
1176
+ return returnValue;
1177
+ }
1178
+
1179
+ // Initializing a new instance of spectrum
1180
+ return this.spectrum("destroy").each(function () {
1181
+ var options = $.extend({}, opts, $(this).data());
1182
+ var spect = spectrum(this, options);
1183
+ $(this).data(dataID, spect.id);
1184
+ });
1185
+ };
1186
+
1187
+ $.fn.spectrum.load = true;
1188
+ $.fn.spectrum.loadOpts = {};
1189
+ $.fn.spectrum.draggable = draggable;
1190
+ $.fn.spectrum.defaults = defaultOpts;
1191
+ $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() {
1192
+ if (typeof inputTypeColorSupport._cachedResult === "undefined") {
1193
+ var colorInput = $("<input type='color'/>")[0]; // if color element is supported, value will default to not null
1194
+ inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== "";
1195
+ }
1196
+ return inputTypeColorSupport._cachedResult;
1197
+ };
1198
+
1199
+ $.spectrum = { };
1200
+ $.spectrum.localization = { };
1201
+ $.spectrum.palettes = { };
1202
+
1203
+ $.fn.spectrum.processNativeColorInputs = function () {
1204
+ var colorInputs = $("input[type=color]");
1205
+ if (colorInputs.length && !inputTypeColorSupport()) {
1206
+ colorInputs.spectrum({
1207
+ preferredFormat: "hex6"
1208
+ });
1209
+ }
1210
+ };
1211
+
1212
+ // TinyColor v1.1.2
1213
+ // https://github.com/bgrins/TinyColor
1214
+ // Brian Grinstead, MIT License
1215
+
1216
+ (function() {
1217
+
1218
+ var trimLeft = /^[\s,#]+/,
1219
+ trimRight = /\s+$/,
1220
+ tinyCounter = 0,
1221
+ math = Math,
1222
+ mathRound = math.round,
1223
+ mathMin = math.min,
1224
+ mathMax = math.max,
1225
+ mathRandom = math.random;
1226
+
1227
+ var tinycolor = function(color, opts) {
1228
+
1229
+ color = (color) ? color : '';
1230
+ opts = opts || { };
1231
+
1232
+ // If input is already a tinycolor, return itself
1233
+ if (color instanceof tinycolor) {
1234
+ return color;
1235
+ }
1236
+ // If we are called as a function, call using new instead
1237
+ if (!(this instanceof tinycolor)) {
1238
+ return new tinycolor(color, opts);
1239
+ }
1240
+
1241
+ var rgb = inputToRGB(color);
1242
+ this._originalInput = color,
1243
+ this._r = rgb.r,
1244
+ this._g = rgb.g,
1245
+ this._b = rgb.b,
1246
+ this._a = rgb.a,
1247
+ this._roundA = mathRound(100*this._a) / 100,
1248
+ this._format = opts.format || rgb.format;
1249
+ this._gradientType = opts.gradientType;
1250
+
1251
+ // Don't let the range of [0,255] come back in [0,1].
1252
+ // Potentially lose a little bit of precision here, but will fix issues where
1253
+ // .5 gets interpreted as half of the total, instead of half of 1
1254
+ // If it was supposed to be 128, this was already taken care of by `inputToRgb`
1255
+ if (this._r < 1) { this._r = mathRound(this._r); }
1256
+ if (this._g < 1) { this._g = mathRound(this._g); }
1257
+ if (this._b < 1) { this._b = mathRound(this._b); }
1258
+
1259
+ this._ok = rgb.ok;
1260
+ this._tc_id = tinyCounter++;
1261
+ };
1262
+
1263
+ tinycolor.prototype = {
1264
+ isDark: function() {
1265
+ return this.getBrightness() < 128;
1266
+ },
1267
+ isLight: function() {
1268
+ return !this.isDark();
1269
+ },
1270
+ isValid: function() {
1271
+ return this._ok;
1272
+ },
1273
+ getOriginalInput: function() {
1274
+ return this._originalInput;
1275
+ },
1276
+ getFormat: function() {
1277
+ return this._format;
1278
+ },
1279
+ getAlpha: function() {
1280
+ return this._a;
1281
+ },
1282
+ getBrightness: function() {
1283
+ var rgb = this.toRgb();
1284
+ return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
1285
+ },
1286
+ setAlpha: function(value) {
1287
+ this._a = boundAlpha(value);
1288
+ this._roundA = mathRound(100*this._a) / 100;
1289
+ return this;
1290
+ },
1291
+ toHsv: function() {
1292
+ var hsv = rgbToHsv(this._r, this._g, this._b);
1293
+ return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
1294
+ },
1295
+ toHsvString: function() {
1296
+ var hsv = rgbToHsv(this._r, this._g, this._b);
1297
+ var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
1298
+ return (this._a == 1) ?
1299
+ "hsv(" + h + ", " + s + "%, " + v + "%)" :
1300
+ "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
1301
+ },
1302
+ toHsl: function() {
1303
+ var hsl = rgbToHsl(this._r, this._g, this._b);
1304
+ return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
1305
+ },
1306
+ toHslString: function() {
1307
+ var hsl = rgbToHsl(this._r, this._g, this._b);
1308
+ var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
1309
+ return (this._a == 1) ?
1310
+ "hsl(" + h + ", " + s + "%, " + l + "%)" :
1311
+ "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
1312
+ },
1313
+ toHex: function(allow3Char) {
1314
+ return rgbToHex(this._r, this._g, this._b, allow3Char);
1315
+ },
1316
+ toHexString: function(allow3Char) {
1317
+ return '#' + this.toHex(allow3Char);
1318
+ },
1319
+ toHex8: function() {
1320
+ return rgbaToHex(this._r, this._g, this._b, this._a);
1321
+ },
1322
+ toHex8String: function() {
1323
+ return '#' + this.toHex8();
1324
+ },
1325
+ toRgb: function() {
1326
+ return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
1327
+ },
1328
+ toRgbString: function() {
1329
+ return (this._a == 1) ?
1330
+ "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
1331
+ "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
1332
+ },
1333
+ toPercentageRgb: function() {
1334
+ return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
1335
+ },
1336
+ toPercentageRgbString: function() {
1337
+ return (this._a == 1) ?
1338
+ "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
1339
+ "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
1340
+ },
1341
+ toName: function() {
1342
+ if (this._a === 0) {
1343
+ return "transparent";
1344
+ }
1345
+
1346
+ if (this._a < 1) {
1347
+ return false;
1348
+ }
1349
+
1350
+ return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
1351
+ },
1352
+ toFilter: function(secondColor) {
1353
+ var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
1354
+ var secondHex8String = hex8String;
1355
+ var gradientType = this._gradientType ? "GradientType = 1, " : "";
1356
+
1357
+ if (secondColor) {
1358
+ var s = tinycolor(secondColor);
1359
+ secondHex8String = s.toHex8String();
1360
+ }
1361
+
1362
+ return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
1363
+ },
1364
+ toString: function(format) {
1365
+ var formatSet = !!format;
1366
+ format = format || this._format;
1367
+
1368
+ var formattedString = false;
1369
+ var hasAlpha = this._a < 1 && this._a >= 0;
1370
+ var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
1371
+
1372
+ if (needsAlphaFormat) {
1373
+ // Special case for "transparent", all other non-alpha formats
1374
+ // will return rgba when there is transparency.
1375
+ if (format === "name" && this._a === 0) {
1376
+ return this.toName();
1377
+ }
1378
+ return this.toRgbString();
1379
+ }
1380
+ if (format === "rgb") {
1381
+ formattedString = this.toRgbString();
1382
+ }
1383
+ if (format === "prgb") {
1384
+ formattedString = this.toPercentageRgbString();
1385
+ }
1386
+ if (format === "hex" || format === "hex6") {
1387
+ formattedString = this.toHexString();
1388
+ }
1389
+ if (format === "hex3") {
1390
+ formattedString = this.toHexString(true);
1391
+ }
1392
+ if (format === "hex8") {
1393
+ formattedString = this.toHex8String();
1394
+ }
1395
+ if (format === "name") {
1396
+ formattedString = this.toName();
1397
+ }
1398
+ if (format === "hsl") {
1399
+ formattedString = this.toHslString();
1400
+ }
1401
+ if (format === "hsv") {
1402
+ formattedString = this.toHsvString();
1403
+ }
1404
+
1405
+ return formattedString || this.toHexString();
1406
+ },
1407
+
1408
+ _applyModification: function(fn, args) {
1409
+ var color = fn.apply(null, [this].concat([].slice.call(args)));
1410
+ this._r = color._r;
1411
+ this._g = color._g;
1412
+ this._b = color._b;
1413
+ this.setAlpha(color._a);
1414
+ return this;
1415
+ },
1416
+ lighten: function() {
1417
+ return this._applyModification(lighten, arguments);
1418
+ },
1419
+ brighten: function() {
1420
+ return this._applyModification(brighten, arguments);
1421
+ },
1422
+ darken: function() {
1423
+ return this._applyModification(darken, arguments);
1424
+ },
1425
+ desaturate: function() {
1426
+ return this._applyModification(desaturate, arguments);
1427
+ },
1428
+ saturate: function() {
1429
+ return this._applyModification(saturate, arguments);
1430
+ },
1431
+ greyscale: function() {
1432
+ return this._applyModification(greyscale, arguments);
1433
+ },
1434
+ spin: function() {
1435
+ return this._applyModification(spin, arguments);
1436
+ },
1437
+
1438
+ _applyCombination: function(fn, args) {
1439
+ return fn.apply(null, [this].concat([].slice.call(args)));
1440
+ },
1441
+ analogous: function() {
1442
+ return this._applyCombination(analogous, arguments);
1443
+ },
1444
+ complement: function() {
1445
+ return this._applyCombination(complement, arguments);
1446
+ },
1447
+ monochromatic: function() {
1448
+ return this._applyCombination(monochromatic, arguments);
1449
+ },
1450
+ splitcomplement: function() {
1451
+ return this._applyCombination(splitcomplement, arguments);
1452
+ },
1453
+ triad: function() {
1454
+ return this._applyCombination(triad, arguments);
1455
+ },
1456
+ tetrad: function() {
1457
+ return this._applyCombination(tetrad, arguments);
1458
+ }
1459
+ };
1460
+
1461
+ // If input is an object, force 1 into "1.0" to handle ratios properly
1462
+ // String input requires "1.0" as input, so 1 will be treated as 1
1463
+ tinycolor.fromRatio = function(color, opts) {
1464
+ if (typeof color == "object") {
1465
+ var newColor = {};
1466
+ for (var i in color) {
1467
+ if (color.hasOwnProperty(i)) {
1468
+ if (i === "a") {
1469
+ newColor[i] = color[i];
1470
+ }
1471
+ else {
1472
+ newColor[i] = convertToPercentage(color[i]);
1473
+ }
1474
+ }
1475
+ }
1476
+ color = newColor;
1477
+ }
1478
+
1479
+ return tinycolor(color, opts);
1480
+ };
1481
+
1482
+ // Given a string or object, convert that input to RGB
1483
+ // Possible string inputs:
1484
+ //
1485
+ // "red"
1486
+ // "#f00" or "f00"
1487
+ // "#ff0000" or "ff0000"
1488
+ // "#ff000000" or "ff000000"
1489
+ // "rgb 255 0 0" or "rgb (255, 0, 0)"
1490
+ // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
1491
+ // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
1492
+ // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
1493
+ // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
1494
+ // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
1495
+ // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
1496
+ //
1497
+ function inputToRGB(color) {
1498
+
1499
+ var rgb = { r: 0, g: 0, b: 0 };
1500
+ var a = 1;
1501
+ var ok = false;
1502
+ var format = false;
1503
+
1504
+ if (typeof color == "string") {
1505
+ color = stringInputToObject(color);
1506
+ }
1507
+
1508
+ if (typeof color == "object") {
1509
+ if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
1510
+ rgb = rgbToRgb(color.r, color.g, color.b);
1511
+ ok = true;
1512
+ format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
1513
+ }
1514
+ else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
1515
+ color.s = convertToPercentage(color.s);
1516
+ color.v = convertToPercentage(color.v);
1517
+ rgb = hsvToRgb(color.h, color.s, color.v);
1518
+ ok = true;
1519
+ format = "hsv";
1520
+ }
1521
+ else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
1522
+ color.s = convertToPercentage(color.s);
1523
+ color.l = convertToPercentage(color.l);
1524
+ rgb = hslToRgb(color.h, color.s, color.l);
1525
+ ok = true;
1526
+ format = "hsl";
1527
+ }
1528
+
1529
+ if (color.hasOwnProperty("a")) {
1530
+ a = color.a;
1531
+ }
1532
+ }
1533
+
1534
+ a = boundAlpha(a);
1535
+
1536
+ return {
1537
+ ok: ok,
1538
+ format: color.format || format,
1539
+ r: mathMin(255, mathMax(rgb.r, 0)),
1540
+ g: mathMin(255, mathMax(rgb.g, 0)),
1541
+ b: mathMin(255, mathMax(rgb.b, 0)),
1542
+ a: a
1543
+ };
1544
+ }
1545
+
1546
+
1547
+ // Conversion Functions
1548
+ // --------------------
1549
+
1550
+ // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
1551
+ // <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
1552
+
1553
+ // `rgbToRgb`
1554
+ // Handle bounds / percentage checking to conform to CSS color spec
1555
+ // <http://www.w3.org/TR/css3-color/>
1556
+ // *Assumes:* r, g, b in [0, 255] or [0, 1]
1557
+ // *Returns:* { r, g, b } in [0, 255]
1558
+ function rgbToRgb(r, g, b){
1559
+ return {
1560
+ r: bound01(r, 255) * 255,
1561
+ g: bound01(g, 255) * 255,
1562
+ b: bound01(b, 255) * 255
1563
+ };
1564
+ }
1565
+
1566
+ // `rgbToHsl`
1567
+ // Converts an RGB color value to HSL.
1568
+ // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
1569
+ // *Returns:* { h, s, l } in [0,1]
1570
+ function rgbToHsl(r, g, b) {
1571
+
1572
+ r = bound01(r, 255);
1573
+ g = bound01(g, 255);
1574
+ b = bound01(b, 255);
1575
+
1576
+ var max = mathMax(r, g, b), min = mathMin(r, g, b);
1577
+ var h, s, l = (max + min) / 2;
1578
+
1579
+ if(max == min) {
1580
+ h = s = 0; // achromatic
1581
+ }
1582
+ else {
1583
+ var d = max - min;
1584
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
1585
+ switch(max) {
1586
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1587
+ case g: h = (b - r) / d + 2; break;
1588
+ case b: h = (r - g) / d + 4; break;
1589
+ }
1590
+
1591
+ h /= 6;
1592
+ }
1593
+
1594
+ return { h: h, s: s, l: l };
1595
+ }
1596
+
1597
+ // `hslToRgb`
1598
+ // Converts an HSL color value to RGB.
1599
+ // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
1600
+ // *Returns:* { r, g, b } in the set [0, 255]
1601
+ function hslToRgb(h, s, l) {
1602
+ var r, g, b;
1603
+
1604
+ h = bound01(h, 360);
1605
+ s = bound01(s, 100);
1606
+ l = bound01(l, 100);
1607
+
1608
+ function hue2rgb(p, q, t) {
1609
+ if(t < 0) t += 1;
1610
+ if(t > 1) t -= 1;
1611
+ if(t < 1/6) return p + (q - p) * 6 * t;
1612
+ if(t < 1/2) return q;
1613
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
1614
+ return p;
1615
+ }
1616
+
1617
+ if(s === 0) {
1618
+ r = g = b = l; // achromatic
1619
+ }
1620
+ else {
1621
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1622
+ var p = 2 * l - q;
1623
+ r = hue2rgb(p, q, h + 1/3);
1624
+ g = hue2rgb(p, q, h);
1625
+ b = hue2rgb(p, q, h - 1/3);
1626
+ }
1627
+
1628
+ return { r: r * 255, g: g * 255, b: b * 255 };
1629
+ }
1630
+
1631
+ // `rgbToHsv`
1632
+ // Converts an RGB color value to HSV
1633
+ // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
1634
+ // *Returns:* { h, s, v } in [0,1]
1635
+ function rgbToHsv(r, g, b) {
1636
+
1637
+ r = bound01(r, 255);
1638
+ g = bound01(g, 255);
1639
+ b = bound01(b, 255);
1640
+
1641
+ var max = mathMax(r, g, b), min = mathMin(r, g, b);
1642
+ var h, s, v = max;
1643
+
1644
+ var d = max - min;
1645
+ s = max === 0 ? 0 : d / max;
1646
+
1647
+ if(max == min) {
1648
+ h = 0; // achromatic
1649
+ }
1650
+ else {
1651
+ switch(max) {
1652
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1653
+ case g: h = (b - r) / d + 2; break;
1654
+ case b: h = (r - g) / d + 4; break;
1655
+ }
1656
+ h /= 6;
1657
+ }
1658
+ return { h: h, s: s, v: v };
1659
+ }
1660
+
1661
+ // `hsvToRgb`
1662
+ // Converts an HSV color value to RGB.
1663
+ // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
1664
+ // *Returns:* { r, g, b } in the set [0, 255]
1665
+ function hsvToRgb(h, s, v) {
1666
+
1667
+ h = bound01(h, 360) * 6;
1668
+ s = bound01(s, 100);
1669
+ v = bound01(v, 100);
1670
+
1671
+ var i = math.floor(h),
1672
+ f = h - i,
1673
+ p = v * (1 - s),
1674
+ q = v * (1 - f * s),
1675
+ t = v * (1 - (1 - f) * s),
1676
+ mod = i % 6,
1677
+ r = [v, q, p, p, t, v][mod],
1678
+ g = [t, v, v, q, p, p][mod],
1679
+ b = [p, p, t, v, v, q][mod];
1680
+
1681
+ return { r: r * 255, g: g * 255, b: b * 255 };
1682
+ }
1683
+
1684
+ // `rgbToHex`
1685
+ // Converts an RGB color to hex
1686
+ // Assumes r, g, and b are contained in the set [0, 255]
1687
+ // Returns a 3 or 6 character hex
1688
+ function rgbToHex(r, g, b, allow3Char) {
1689
+
1690
+ var hex = [
1691
+ pad2(mathRound(r).toString(16)),
1692
+ pad2(mathRound(g).toString(16)),
1693
+ pad2(mathRound(b).toString(16))
1694
+ ];
1695
+
1696
+ // Return a 3 character hex if possible
1697
+ if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
1698
+ return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
1699
+ }
1700
+
1701
+ return hex.join("");
1702
+ }
1703
+ // `rgbaToHex`
1704
+ // Converts an RGBA color plus alpha transparency to hex
1705
+ // Assumes r, g, b and a are contained in the set [0, 255]
1706
+ // Returns an 8 character hex
1707
+ function rgbaToHex(r, g, b, a) {
1708
+
1709
+ var hex = [
1710
+ pad2(convertDecimalToHex(a)),
1711
+ pad2(mathRound(r).toString(16)),
1712
+ pad2(mathRound(g).toString(16)),
1713
+ pad2(mathRound(b).toString(16))
1714
+ ];
1715
+
1716
+ return hex.join("");
1717
+ }
1718
+
1719
+ // `equals`
1720
+ // Can be called with any tinycolor input
1721
+ tinycolor.equals = function (color1, color2) {
1722
+ if (!color1 || !color2) { return false; }
1723
+ return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
1724
+ };
1725
+ tinycolor.random = function() {
1726
+ return tinycolor.fromRatio({
1727
+ r: mathRandom(),
1728
+ g: mathRandom(),
1729
+ b: mathRandom()
1730
+ });
1731
+ };
1732
+
1733
+
1734
+ // Modification Functions
1735
+ // ----------------------
1736
+ // Thanks to less.js for some of the basics here
1737
+ // <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
1738
+
1739
+ function desaturate(color, amount) {
1740
+ amount = (amount === 0) ? 0 : (amount || 10);
1741
+ var hsl = tinycolor(color).toHsl();
1742
+ hsl.s -= amount / 100;
1743
+ hsl.s = clamp01(hsl.s);
1744
+ return tinycolor(hsl);
1745
+ }
1746
+
1747
+ function saturate(color, amount) {
1748
+ amount = (amount === 0) ? 0 : (amount || 10);
1749
+ var hsl = tinycolor(color).toHsl();
1750
+ hsl.s += amount / 100;
1751
+ hsl.s = clamp01(hsl.s);
1752
+ return tinycolor(hsl);
1753
+ }
1754
+
1755
+ function greyscale(color) {
1756
+ return tinycolor(color).desaturate(100);
1757
+ }
1758
+
1759
+ function lighten (color, amount) {
1760
+ amount = (amount === 0) ? 0 : (amount || 10);
1761
+ var hsl = tinycolor(color).toHsl();
1762
+ hsl.l += amount / 100;
1763
+ hsl.l = clamp01(hsl.l);
1764
+ return tinycolor(hsl);
1765
+ }
1766
+
1767
+ function brighten(color, amount) {
1768
+ amount = (amount === 0) ? 0 : (amount || 10);
1769
+ var rgb = tinycolor(color).toRgb();
1770
+ rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
1771
+ rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
1772
+ rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
1773
+ return tinycolor(rgb);
1774
+ }
1775
+
1776
+ function darken (color, amount) {
1777
+ amount = (amount === 0) ? 0 : (amount || 10);
1778
+ var hsl = tinycolor(color).toHsl();
1779
+ hsl.l -= amount / 100;
1780
+ hsl.l = clamp01(hsl.l);
1781
+ return tinycolor(hsl);
1782
+ }
1783
+
1784
+ // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
1785
+ // Values outside of this range will be wrapped into this range.
1786
+ function spin(color, amount) {
1787
+ var hsl = tinycolor(color).toHsl();
1788
+ var hue = (mathRound(hsl.h) + amount) % 360;
1789
+ hsl.h = hue < 0 ? 360 + hue : hue;
1790
+ return tinycolor(hsl);
1791
+ }
1792
+
1793
+ // Combination Functions
1794
+ // ---------------------
1795
+ // Thanks to jQuery xColor for some of the ideas behind these
1796
+ // <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
1797
+
1798
+ function complement(color) {
1799
+ var hsl = tinycolor(color).toHsl();
1800
+ hsl.h = (hsl.h + 180) % 360;
1801
+ return tinycolor(hsl);
1802
+ }
1803
+
1804
+ function triad(color) {
1805
+ var hsl = tinycolor(color).toHsl();
1806
+ var h = hsl.h;
1807
+ return [
1808
+ tinycolor(color),
1809
+ tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
1810
+ tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
1811
+ ];
1812
+ }
1813
+
1814
+ function tetrad(color) {
1815
+ var hsl = tinycolor(color).toHsl();
1816
+ var h = hsl.h;
1817
+ return [
1818
+ tinycolor(color),
1819
+ tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
1820
+ tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
1821
+ tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
1822
+ ];
1823
+ }
1824
+
1825
+ function splitcomplement(color) {
1826
+ var hsl = tinycolor(color).toHsl();
1827
+ var h = hsl.h;
1828
+ return [
1829
+ tinycolor(color),
1830
+ tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
1831
+ tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
1832
+ ];
1833
+ }
1834
+
1835
+ function analogous(color, results, slices) {
1836
+ results = results || 6;
1837
+ slices = slices || 30;
1838
+
1839
+ var hsl = tinycolor(color).toHsl();
1840
+ var part = 360 / slices;
1841
+ var ret = [tinycolor(color)];
1842
+
1843
+ for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
1844
+ hsl.h = (hsl.h + part) % 360;
1845
+ ret.push(tinycolor(hsl));
1846
+ }
1847
+ return ret;
1848
+ }
1849
+
1850
+ function monochromatic(color, results) {
1851
+ results = results || 6;
1852
+ var hsv = tinycolor(color).toHsv();
1853
+ var h = hsv.h, s = hsv.s, v = hsv.v;
1854
+ var ret = [];
1855
+ var modification = 1 / results;
1856
+
1857
+ while (results--) {
1858
+ ret.push(tinycolor({ h: h, s: s, v: v}));
1859
+ v = (v + modification) % 1;
1860
+ }
1861
+
1862
+ return ret;
1863
+ }
1864
+
1865
+ // Utility Functions
1866
+ // ---------------------
1867
+
1868
+ tinycolor.mix = function(color1, color2, amount) {
1869
+ amount = (amount === 0) ? 0 : (amount || 50);
1870
+
1871
+ var rgb1 = tinycolor(color1).toRgb();
1872
+ var rgb2 = tinycolor(color2).toRgb();
1873
+
1874
+ var p = amount / 100;
1875
+ var w = p * 2 - 1;
1876
+ var a = rgb2.a - rgb1.a;
1877
+
1878
+ var w1;
1879
+
1880
+ if (w * a == -1) {
1881
+ w1 = w;
1882
+ } else {
1883
+ w1 = (w + a) / (1 + w * a);
1884
+ }
1885
+
1886
+ w1 = (w1 + 1) / 2;
1887
+
1888
+ var w2 = 1 - w1;
1889
+
1890
+ var rgba = {
1891
+ r: rgb2.r * w1 + rgb1.r * w2,
1892
+ g: rgb2.g * w1 + rgb1.g * w2,
1893
+ b: rgb2.b * w1 + rgb1.b * w2,
1894
+ a: rgb2.a * p + rgb1.a * (1 - p)
1895
+ };
1896
+
1897
+ return tinycolor(rgba);
1898
+ };
1899
+
1900
+
1901
+ // Readability Functions
1902
+ // ---------------------
1903
+ // <http://www.w3.org/TR/AERT#color-contrast>
1904
+
1905
+ // `readability`
1906
+ // Analyze the 2 colors and returns an object with the following properties:
1907
+ // `brightness`: difference in brightness between the two colors
1908
+ // `color`: difference in color/hue between the two colors
1909
+ tinycolor.readability = function(color1, color2) {
1910
+ var c1 = tinycolor(color1);
1911
+ var c2 = tinycolor(color2);
1912
+ var rgb1 = c1.toRgb();
1913
+ var rgb2 = c2.toRgb();
1914
+ var brightnessA = c1.getBrightness();
1915
+ var brightnessB = c2.getBrightness();
1916
+ var colorDiff = (
1917
+ Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +
1918
+ Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +
1919
+ Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)
1920
+ );
1921
+
1922
+ return {
1923
+ brightness: Math.abs(brightnessA - brightnessB),
1924
+ color: colorDiff
1925
+ };
1926
+ };
1927
+
1928
+ // `readable`
1929
+ // http://www.w3.org/TR/AERT#color-contrast
1930
+ // Ensure that foreground and background color combinations provide sufficient contrast.
1931
+ // *Example*
1932
+ // tinycolor.isReadable("#000", "#111") => false
1933
+ tinycolor.isReadable = function(color1, color2) {
1934
+ var readability = tinycolor.readability(color1, color2);
1935
+ return readability.brightness > 125 && readability.color > 500;
1936
+ };
1937
+
1938
+ // `mostReadable`
1939
+ // Given a base color and a list of possible foreground or background
1940
+ // colors for that base, returns the most readable color.
1941
+ // *Example*
1942
+ // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
1943
+ tinycolor.mostReadable = function(baseColor, colorList) {
1944
+ var bestColor = null;
1945
+ var bestScore = 0;
1946
+ var bestIsReadable = false;
1947
+ for (var i=0; i < colorList.length; i++) {
1948
+
1949
+ // We normalize both around the "acceptable" breaking point,
1950
+ // but rank brightness constrast higher than hue.
1951
+
1952
+ var readability = tinycolor.readability(baseColor, colorList[i]);
1953
+ var readable = readability.brightness > 125 && readability.color > 500;
1954
+ var score = 3 * (readability.brightness / 125) + (readability.color / 500);
1955
+
1956
+ if ((readable && ! bestIsReadable) ||
1957
+ (readable && bestIsReadable && score > bestScore) ||
1958
+ ((! readable) && (! bestIsReadable) && score > bestScore)) {
1959
+ bestIsReadable = readable;
1960
+ bestScore = score;
1961
+ bestColor = tinycolor(colorList[i]);
1962
+ }
1963
+ }
1964
+ return bestColor;
1965
+ };
1966
+
1967
+
1968
+ // Big List of Colors
1969
+ // ------------------
1970
+ // <http://www.w3.org/TR/css3-color/#svg-color>
1971
+ var names = tinycolor.names = {
1972
+ aliceblue: "f0f8ff",
1973
+ antiquewhite: "faebd7",
1974
+ aqua: "0ff",
1975
+ aquamarine: "7fffd4",
1976
+ azure: "f0ffff",
1977
+ beige: "f5f5dc",
1978
+ bisque: "ffe4c4",
1979
+ black: "000",
1980
+ blanchedalmond: "ffebcd",
1981
+ blue: "00f",
1982
+ blueviolet: "8a2be2",
1983
+ brown: "a52a2a",
1984
+ burlywood: "deb887",
1985
+ burntsienna: "ea7e5d",
1986
+ cadetblue: "5f9ea0",
1987
+ chartreuse: "7fff00",
1988
+ chocolate: "d2691e",
1989
+ coral: "ff7f50",
1990
+ cornflowerblue: "6495ed",
1991
+ cornsilk: "fff8dc",
1992
+ crimson: "dc143c",
1993
+ cyan: "0ff",
1994
+ darkblue: "00008b",
1995
+ darkcyan: "008b8b",
1996
+ darkgoldenrod: "b8860b",
1997
+ darkgray: "a9a9a9",
1998
+ darkgreen: "006400",
1999
+ darkgrey: "a9a9a9",
2000
+ darkkhaki: "bdb76b",
2001
+ darkmagenta: "8b008b",
2002
+ darkolivegreen: "556b2f",
2003
+ darkorange: "ff8c00",
2004
+ darkorchid: "9932cc",
2005
+ darkred: "8b0000",
2006
+ darksalmon: "e9967a",
2007
+ darkseagreen: "8fbc8f",
2008
+ darkslateblue: "483d8b",
2009
+ darkslategray: "2f4f4f",
2010
+ darkslategrey: "2f4f4f",
2011
+ darkturquoise: "00ced1",
2012
+ darkviolet: "9400d3",
2013
+ deeppink: "ff1493",
2014
+ deepskyblue: "00bfff",
2015
+ dimgray: "696969",
2016
+ dimgrey: "696969",
2017
+ dodgerblue: "1e90ff",
2018
+ firebrick: "b22222",
2019
+ floralwhite: "fffaf0",
2020
+ forestgreen: "228b22",
2021
+ fuchsia: "f0f",
2022
+ gainsboro: "dcdcdc",
2023
+ ghostwhite: "f8f8ff",
2024
+ gold: "ffd700",
2025
+ goldenrod: "daa520",
2026
+ gray: "808080",
2027
+ green: "008000",
2028
+ greenyellow: "adff2f",
2029
+ grey: "808080",
2030
+ honeydew: "f0fff0",
2031
+ hotpink: "ff69b4",
2032
+ indianred: "cd5c5c",
2033
+ indigo: "4b0082",
2034
+ ivory: "fffff0",
2035
+ khaki: "f0e68c",
2036
+ lavender: "e6e6fa",
2037
+ lavenderblush: "fff0f5",
2038
+ lawngreen: "7cfc00",
2039
+ lemonchiffon: "fffacd",
2040
+ lightblue: "add8e6",
2041
+ lightcoral: "f08080",
2042
+ lightcyan: "e0ffff",
2043
+ lightgoldenrodyellow: "fafad2",
2044
+ lightgray: "d3d3d3",
2045
+ lightgreen: "90ee90",
2046
+ lightgrey: "d3d3d3",
2047
+ lightpink: "ffb6c1",
2048
+ lightsalmon: "ffa07a",
2049
+ lightseagreen: "20b2aa",
2050
+ lightskyblue: "87cefa",
2051
+ lightslategray: "789",
2052
+ lightslategrey: "789",
2053
+ lightsteelblue: "b0c4de",
2054
+ lightyellow: "ffffe0",
2055
+ lime: "0f0",
2056
+ limegreen: "32cd32",
2057
+ linen: "faf0e6",
2058
+ magenta: "f0f",
2059
+ maroon: "800000",
2060
+ mediumaquamarine: "66cdaa",
2061
+ mediumblue: "0000cd",
2062
+ mediumorchid: "ba55d3",
2063
+ mediumpurple: "9370db",
2064
+ mediumseagreen: "3cb371",
2065
+ mediumslateblue: "7b68ee",
2066
+ mediumspringgreen: "00fa9a",
2067
+ mediumturquoise: "48d1cc",
2068
+ mediumvioletred: "c71585",
2069
+ midnightblue: "191970",
2070
+ mintcream: "f5fffa",
2071
+ mistyrose: "ffe4e1",
2072
+ moccasin: "ffe4b5",
2073
+ navajowhite: "ffdead",
2074
+ navy: "000080",
2075
+ oldlace: "fdf5e6",
2076
+ olive: "808000",
2077
+ olivedrab: "6b8e23",
2078
+ orange: "ffa500",
2079
+ orangered: "ff4500",
2080
+ orchid: "da70d6",
2081
+ palegoldenrod: "eee8aa",
2082
+ palegreen: "98fb98",
2083
+ paleturquoise: "afeeee",
2084
+ palevioletred: "db7093",
2085
+ papayawhip: "ffefd5",
2086
+ peachpuff: "ffdab9",
2087
+ peru: "cd853f",
2088
+ pink: "ffc0cb",
2089
+ plum: "dda0dd",
2090
+ powderblue: "b0e0e6",
2091
+ purple: "800080",
2092
+ rebeccapurple: "663399",
2093
+ red: "f00",
2094
+ rosybrown: "bc8f8f",
2095
+ royalblue: "4169e1",
2096
+ saddlebrown: "8b4513",
2097
+ salmon: "fa8072",
2098
+ sandybrown: "f4a460",
2099
+ seagreen: "2e8b57",
2100
+ seashell: "fff5ee",
2101
+ sienna: "a0522d",
2102
+ silver: "c0c0c0",
2103
+ skyblue: "87ceeb",
2104
+ slateblue: "6a5acd",
2105
+ slategray: "708090",
2106
+ slategrey: "708090",
2107
+ snow: "fffafa",
2108
+ springgreen: "00ff7f",
2109
+ steelblue: "4682b4",
2110
+ tan: "d2b48c",
2111
+ teal: "008080",
2112
+ thistle: "d8bfd8",
2113
+ tomato: "ff6347",
2114
+ turquoise: "40e0d0",
2115
+ violet: "ee82ee",
2116
+ wheat: "f5deb3",
2117
+ white: "fff",
2118
+ whitesmoke: "f5f5f5",
2119
+ yellow: "ff0",
2120
+ yellowgreen: "9acd32"
2121
+ };
2122
+
2123
+ // Make it easy to access colors via `hexNames[hex]`
2124
+ var hexNames = tinycolor.hexNames = flip(names);
2125
+
2126
+
2127
+ // Utilities
2128
+ // ---------
2129
+
2130
+ // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
2131
+ function flip(o) {
2132
+ var flipped = { };
2133
+ for (var i in o) {
2134
+ if (o.hasOwnProperty(i)) {
2135
+ flipped[o[i]] = i;
2136
+ }
2137
+ }
2138
+ return flipped;
2139
+ }
2140
+
2141
+ // Return a valid alpha value [0,1] with all invalid values being set to 1
2142
+ function boundAlpha(a) {
2143
+ a = parseFloat(a);
2144
+
2145
+ if (isNaN(a) || a < 0 || a > 1) {
2146
+ a = 1;
2147
+ }
2148
+
2149
+ return a;
2150
+ }
2151
+
2152
+ // Take input from [0, n] and return it as [0, 1]
2153
+ function bound01(n, max) {
2154
+ if (isOnePointZero(n)) { n = "100%"; }
2155
+
2156
+ var processPercent = isPercentage(n);
2157
+ n = mathMin(max, mathMax(0, parseFloat(n)));
2158
+
2159
+ // Automatically convert percentage into number
2160
+ if (processPercent) {
2161
+ n = parseInt(n * max, 10) / 100;
2162
+ }
2163
+
2164
+ // Handle floating point rounding errors
2165
+ if ((math.abs(n - max) < 0.000001)) {
2166
+ return 1;
2167
+ }
2168
+
2169
+ // Convert into [0, 1] range if it isn't already
2170
+ return (n % max) / parseFloat(max);
2171
+ }
2172
+
2173
+ // Force a number between 0 and 1
2174
+ function clamp01(val) {
2175
+ return mathMin(1, mathMax(0, val));
2176
+ }
2177
+
2178
+ // Parse a base-16 hex value into a base-10 integer
2179
+ function parseIntFromHex(val) {
2180
+ return parseInt(val, 16);
2181
+ }
2182
+
2183
+ // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
2184
+ // <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
2185
+ function isOnePointZero(n) {
2186
+ return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
2187
+ }
2188
+
2189
+ // Check to see if string passed in is a percentage
2190
+ function isPercentage(n) {
2191
+ return typeof n === "string" && n.indexOf('%') != -1;
2192
+ }
2193
+
2194
+ // Force a hex value to have 2 characters
2195
+ function pad2(c) {
2196
+ return c.length == 1 ? '0' + c : '' + c;
2197
+ }
2198
+
2199
+ // Replace a decimal with it's percentage value
2200
+ function convertToPercentage(n) {
2201
+ if (n <= 1) {
2202
+ n = (n * 100) + "%";
2203
+ }
2204
+
2205
+ return n;
2206
+ }
2207
+
2208
+ // Converts a decimal to a hex value
2209
+ function convertDecimalToHex(d) {
2210
+ return Math.round(parseFloat(d) * 255).toString(16);
2211
+ }
2212
+ // Converts a hex value to a decimal
2213
+ function convertHexToDecimal(h) {
2214
+ return (parseIntFromHex(h) / 255);
2215
+ }
2216
+
2217
+ var matchers = (function() {
2218
+
2219
+ // <http://www.w3.org/TR/css3-values/#integers>
2220
+ var CSS_INTEGER = "[-\\+]?\\d+%?";
2221
+
2222
+ // <http://www.w3.org/TR/css3-values/#number-value>
2223
+ var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
2224
+
2225
+ // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
2226
+ var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
2227
+
2228
+ // Actual matching.
2229
+ // Parentheses and commas are optional, but not required.
2230
+ // Whitespace can take the place of commas or opening paren
2231
+ var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
2232
+ var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
2233
+
2234
+ return {
2235
+ rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
2236
+ rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
2237
+ hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
2238
+ hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
2239
+ hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
2240
+ hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
2241
+ hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
2242
+ hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
2243
+ hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
2244
+ };
2245
+ })();
2246
+
2247
+ // `stringInputToObject`
2248
+ // Permissive string parsing. Take in a number of formats, and output an object
2249
+ // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
2250
+ function stringInputToObject(color) {
2251
+
2252
+ color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
2253
+ var named = false;
2254
+ if (names[color]) {
2255
+ color = names[color];
2256
+ named = true;
2257
+ }
2258
+ else if (color == 'transparent') {
2259
+ return { r: 0, g: 0, b: 0, a: 0, format: "name" };
2260
+ }
2261
+
2262
+ // Try to match string input using regular expressions.
2263
+ // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
2264
+ // Just return an object and let the conversion functions handle that.
2265
+ // This way the result will be the same whether the tinycolor is initialized with string or object.
2266
+ var match;
2267
+ if ((match = matchers.rgb.exec(color))) {
2268
+ return { r: match[1], g: match[2], b: match[3] };
2269
+ }
2270
+ if ((match = matchers.rgba.exec(color))) {
2271
+ return { r: match[1], g: match[2], b: match[3], a: match[4] };
2272
+ }
2273
+ if ((match = matchers.hsl.exec(color))) {
2274
+ return { h: match[1], s: match[2], l: match[3] };
2275
+ }
2276
+ if ((match = matchers.hsla.exec(color))) {
2277
+ return { h: match[1], s: match[2], l: match[3], a: match[4] };
2278
+ }
2279
+ if ((match = matchers.hsv.exec(color))) {
2280
+ return { h: match[1], s: match[2], v: match[3] };
2281
+ }
2282
+ if ((match = matchers.hsva.exec(color))) {
2283
+ return { h: match[1], s: match[2], v: match[3], a: match[4] };
2284
+ }
2285
+ if ((match = matchers.hex8.exec(color))) {
2286
+ return {
2287
+ a: convertHexToDecimal(match[1]),
2288
+ r: parseIntFromHex(match[2]),
2289
+ g: parseIntFromHex(match[3]),
2290
+ b: parseIntFromHex(match[4]),
2291
+ format: named ? "name" : "hex8"
2292
+ };
2293
+ }
2294
+ if ((match = matchers.hex6.exec(color))) {
2295
+ return {
2296
+ r: parseIntFromHex(match[1]),
2297
+ g: parseIntFromHex(match[2]),
2298
+ b: parseIntFromHex(match[3]),
2299
+ format: named ? "name" : "hex"
2300
+ };
2301
+ }
2302
+ if ((match = matchers.hex3.exec(color))) {
2303
+ return {
2304
+ r: parseIntFromHex(match[1] + '' + match[1]),
2305
+ g: parseIntFromHex(match[2] + '' + match[2]),
2306
+ b: parseIntFromHex(match[3] + '' + match[3]),
2307
+ format: named ? "name" : "hex"
2308
+ };
2309
+ }
2310
+
2311
+ return false;
2312
+ }
2313
+
2314
+ window.tinycolor = tinycolor;
2315
+ })();
2316
+
2317
+ $(function () {
2318
+ if ($.fn.spectrum.load) {
2319
+ $.fn.spectrum.processNativeColorInputs();
2320
+ }
2321
+ });
2322
+
2323
+ });
media/js/lib/spectrum.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ // Spectrum Colorpicker v1.8.0
2
+ // https://github.com/bgrins/spectrum
3
+ // Author: Brian Grinstead
4
+ // License: MIT
5
+ !function(t){"use strict";"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof exports&&"object"==typeof module?module.exports=t(require("jquery")):t(jQuery)}(function(t,e){"use strict";function r(e,r,n,a){for(var i=[],s=0;s<e.length;s++){var o=e[s];if(o){var l=tinycolor(o),c=l.toHsl().l<.5?"sp-thumb-el sp-thumb-dark":"sp-thumb-el sp-thumb-light";c+=tinycolor.equals(r,o)?" sp-thumb-active":"";var f=l.toString(a.preferredFormat||"rgb"),u=b?"background-color:"+l.toRgbString():"filter:"+l.toFilter();i.push('<span title="'+f+'" data-color="'+l.toRgbString()+'" class="'+c+'"><span class="sp-thumb-inner" style="'+u+';" /></span>')}else{var h="sp-clear-display";i.push(t("<div />").append(t('<span data-color="" style="background-color:transparent;" class="'+h+'"></span>').attr("title",a.noColorSelectedText)).html())}}return"<div class='sp-cf "+n+"'>"+i.join("")+"</div>"}function n(){for(var t=0;t<p.length;t++)p[t]&&p[t].hide()}function a(e,r){var n=t.extend({},d,e);return n.callbacks={move:c(n.move,r),change:c(n.change,r),show:c(n.show,r),hide:c(n.hide,r),beforeShow:c(n.beforeShow,r)},n}function i(i,o){function c(){if(W.showPaletteOnly&&(W.showPalette=!0),Dt.text(W.showPaletteOnly?W.togglePaletteMoreText:W.togglePaletteLessText),W.palette){dt=W.palette.slice(0),pt=t.isArray(dt[0])?dt:[dt],gt={};for(var e=0;e<pt.length;e++)for(var r=0;r<pt[e].length;r++){var n=tinycolor(pt[e][r]).toRgbString();gt[n]=!0}}kt.toggleClass("sp-flat",X),kt.toggleClass("sp-input-disabled",!W.showInput),kt.toggleClass("sp-alpha-enabled",W.showAlpha),kt.toggleClass("sp-clear-enabled",Qt),kt.toggleClass("sp-buttons-disabled",!W.showButtons),kt.toggleClass("sp-palette-buttons-disabled",!W.togglePaletteOnly),kt.toggleClass("sp-palette-disabled",!W.showPalette),kt.toggleClass("sp-palette-only",W.showPaletteOnly),kt.toggleClass("sp-initial-disabled",!W.showInitial),kt.addClass(W.className).addClass(W.containerClassName),z()}function d(){function e(e){return e.data&&e.data.ignore?(O(t(e.target).closest(".sp-thumb-el").data("color")),j()):(O(t(e.target).closest(".sp-thumb-el").data("color")),j(),I(!0),W.hideAfterPaletteSelect&&F()),!1}if(g&&kt.find("*:not(input)").attr("unselectable","on"),c(),Bt&&_t.after(Lt).hide(),Qt||jt.hide(),X)_t.after(kt).hide();else{var r="parent"===W.appendTo?_t.parent():t(W.appendTo);1!==r.length&&(r=t("body")),r.append(kt)}y(),Kt.bind("click.spectrum touchstart.spectrum",function(e){xt||A(),e.stopPropagation(),t(e.target).is("input")||e.preventDefault()}),(_t.is(":disabled")||W.disabled===!0)&&V(),kt.click(l),Tt.change(P),Tt.bind("paste",function(){setTimeout(P,1)}),Tt.keydown(function(t){13==t.keyCode&&P()}),Nt.text(W.cancelText),Nt.bind("click.spectrum",function(t){t.stopPropagation(),t.preventDefault(),T(),F()}),jt.attr("title",W.clearText),jt.bind("click.spectrum",function(t){t.stopPropagation(),t.preventDefault(),Gt=!0,j(),X&&I(!0)}),Et.text(W.chooseText),Et.bind("click.spectrum",function(t){t.stopPropagation(),t.preventDefault(),g&&Tt.is(":focus")&&Tt.trigger("change"),N()&&(I(!0),F())}),Dt.text(W.showPaletteOnly?W.togglePaletteMoreText:W.togglePaletteLessText),Dt.bind("click.spectrum",function(t){t.stopPropagation(),t.preventDefault(),W.showPaletteOnly=!W.showPaletteOnly,W.showPaletteOnly||X||kt.css("left","-="+(St.outerWidth(!0)+5)),c()}),f(Ht,function(t,e,r){ht=t/st,Gt=!1,r.shiftKey&&(ht=Math.round(10*ht)/10),j()},S,C),f(At,function(t,e){ct=parseFloat(e/at),Gt=!1,W.showAlpha||(ht=1),j()},S,C),f(Ct,function(t,e,r){if(r.shiftKey){if(!yt){var n=ft*et,a=rt-ut*rt,i=Math.abs(t-n)>Math.abs(e-a);yt=i?"x":"y"}}else yt=null;var s=!yt||"x"===yt,o=!yt||"y"===yt;s&&(ft=parseFloat(t/et)),o&&(ut=parseFloat((rt-e)/rt)),Gt=!1,W.showAlpha||(ht=1),j()},S,C),$t?(O($t),E(),Xt=W.preferredFormat||tinycolor($t).format,w($t)):E(),X&&M();var n=g?"mousedown.spectrum":"click.spectrum touchstart.spectrum";Ot.delegate(".sp-thumb-el",n,e),qt.delegate(".sp-thumb-el:nth-child(1)",n,{ignore:!0},e)}function y(){if(G&&window.localStorage){try{var e=window.localStorage[G].split(",#");e.length>1&&(delete window.localStorage[G],t.each(e,function(t,e){w(e)}))}catch(r){}try{bt=window.localStorage[G].split(";")}catch(r){}}}function w(e){if(Y){var r=tinycolor(e).toRgbString();if(!gt[r]&&-1===t.inArray(r,bt))for(bt.push(r);bt.length>vt;)bt.shift();if(G&&window.localStorage)try{window.localStorage[G]=bt.join(";")}catch(n){}}}function _(){var t=[];if(W.showPalette)for(var e=0;e<bt.length;e++){var r=tinycolor(bt[e]).toRgbString();gt[r]||t.push(bt[e])}return t.reverse().slice(0,W.maxSelectionSize)}function x(){var e=q(),n=t.map(pt,function(t,n){return r(t,e,"sp-palette-row sp-palette-row-"+n,W)});y(),bt&&n.push(r(_(),e,"sp-palette-row sp-palette-row-selection",W)),Ot.html(n.join(""))}function k(){if(W.showInitial){var t=Wt,e=q();qt.html(r([t,e],e,"sp-palette-row-initial",W))}}function S(){(0>=rt||0>=et||0>=at)&&z(),tt=!0,kt.addClass(mt),yt=null,_t.trigger("dragstart.spectrum",[q()])}function C(){tt=!1,kt.removeClass(mt),_t.trigger("dragstop.spectrum",[q()])}function P(){var t=Tt.val();if(null!==t&&""!==t||!Qt){var e=tinycolor(t);e.isValid()?(O(e),I(!0)):Tt.addClass("sp-validation-error")}else O(null),I(!0)}function A(){Z?F():M()}function M(){var e=t.Event("beforeShow.spectrum");return Z?void z():(_t.trigger(e,[q()]),void(J.beforeShow(q())===!1||e.isDefaultPrevented()||(n(),Z=!0,t(wt).bind("keydown.spectrum",R),t(wt).bind("click.spectrum",H),t(window).bind("resize.spectrum",U),Lt.addClass("sp-active"),kt.removeClass("sp-hidden"),z(),E(),Wt=q(),k(),J.show(Wt),_t.trigger("show.spectrum",[Wt]))))}function R(t){27===t.keyCode&&F()}function H(t){2!=t.button&&(tt||(Yt?I(!0):T(),F()))}function F(){Z&&!X&&(Z=!1,t(wt).unbind("keydown.spectrum",R),t(wt).unbind("click.spectrum",H),t(window).unbind("resize.spectrum",U),Lt.removeClass("sp-active"),kt.addClass("sp-hidden"),J.hide(q()),_t.trigger("hide.spectrum",[q()]))}function T(){O(Wt,!0)}function O(t,e){if(tinycolor.equals(t,q()))return void E();var r,n;!t&&Qt?Gt=!0:(Gt=!1,r=tinycolor(t),n=r.toHsv(),ct=n.h%360/360,ft=n.s,ut=n.v,ht=n.a),E(),r&&r.isValid()&&!e&&(Xt=W.preferredFormat||r.getFormat())}function q(t){return t=t||{},Qt&&Gt?null:tinycolor.fromRatio({h:ct,s:ft,v:ut,a:Math.round(100*ht)/100},{format:t.format||Xt})}function N(){return!Tt.hasClass("sp-validation-error")}function j(){E(),J.move(q()),_t.trigger("move.spectrum",[q()])}function E(){Tt.removeClass("sp-validation-error"),D();var t=tinycolor.fromRatio({h:ct,s:1,v:1});Ct.css("background-color",t.toHexString());var e=Xt;1>ht&&(0!==ht||"name"!==e)&&("hex"===e||"hex3"===e||"hex6"===e||"name"===e)&&(e="rgb");var r=q({format:e}),n="";if(Vt.removeClass("sp-clear-display"),Vt.css("background-color","transparent"),!r&&Qt)Vt.addClass("sp-clear-display");else{var a=r.toHexString(),i=r.toRgbString();if(b||1===r.alpha?Vt.css("background-color",i):(Vt.css("background-color","transparent"),Vt.css("filter",r.toFilter())),W.showAlpha){var s=r.toRgb();s.a=0;var o=tinycolor(s).toRgbString(),l="linear-gradient(left, "+o+", "+a+")";g?Rt.css("filter",tinycolor(o).toFilter({gradientType:1},a)):(Rt.css("background","-webkit-"+l),Rt.css("background","-moz-"+l),Rt.css("background","-ms-"+l),Rt.css("background","linear-gradient(to right, "+o+", "+a+")"))}n=r.toString(e)}W.showInput&&Tt.val(n),W.showPalette&&x(),k()}function D(){var t=ft,e=ut;if(Qt&&Gt)Ft.hide(),Mt.hide(),Pt.hide();else{Ft.show(),Mt.show(),Pt.show();var r=t*et,n=rt-e*rt;r=Math.max(-nt,Math.min(et-nt,r-nt)),n=Math.max(-nt,Math.min(rt-nt,n-nt)),Pt.css({top:n+"px",left:r+"px"});var a=ht*st;Ft.css({left:a-ot/2+"px"});var i=ct*at;Mt.css({top:i-lt+"px"})}}function I(t){var e=q(),r="",n=!tinycolor.equals(e,Wt);e&&(r=e.toString(Xt),w(e)),It&&_t.val(r),t&&n&&(J.change(e),_t.trigger("change",[e]))}function z(){Z&&(et=Ct.width(),rt=Ct.height(),nt=Pt.height(),it=At.width(),at=At.height(),lt=Mt.height(),st=Ht.width(),ot=Ft.width(),X||(kt.css("position","absolute"),W.offset?kt.offset(W.offset):kt.offset(s(kt,Kt))),D(),W.showPalette&&x(),_t.trigger("reflow.spectrum"))}function B(){_t.show(),Kt.unbind("click.spectrum touchstart.spectrum"),kt.remove(),Lt.remove(),p[Jt.id]=null}function L(r,n){return r===e?t.extend({},W):n===e?W[r]:(W[r]=n,"preferredFormat"===r&&(Xt=W.preferredFormat),void c())}function K(){xt=!1,_t.attr("disabled",!1),Kt.removeClass("sp-disabled")}function V(){F(),xt=!0,_t.attr("disabled",!0),Kt.addClass("sp-disabled")}function $(t){W.offset=t,z()}var W=a(o,i),X=W.flat,Y=W.showSelectionPalette,G=W.localStorageKey,Q=W.theme,J=W.callbacks,U=u(z,10),Z=!1,tt=!1,et=0,rt=0,nt=0,at=0,it=0,st=0,ot=0,lt=0,ct=0,ft=0,ut=0,ht=1,dt=[],pt=[],gt={},bt=W.selectionPalette.slice(0),vt=W.maxSelectionSize,mt="sp-dragging",yt=null,wt=i.ownerDocument,_t=(wt.body,t(i)),xt=!1,kt=t(m,wt).addClass(Q),St=kt.find(".sp-picker-container"),Ct=kt.find(".sp-color"),Pt=kt.find(".sp-dragger"),At=kt.find(".sp-hue"),Mt=kt.find(".sp-slider"),Rt=kt.find(".sp-alpha-inner"),Ht=kt.find(".sp-alpha"),Ft=kt.find(".sp-alpha-handle"),Tt=kt.find(".sp-input"),Ot=kt.find(".sp-palette"),qt=kt.find(".sp-initial"),Nt=kt.find(".sp-cancel"),jt=kt.find(".sp-clear"),Et=kt.find(".sp-choose"),Dt=kt.find(".sp-palette-toggle"),It=_t.is("input"),zt=It&&"color"===_t.attr("type")&&h(),Bt=It&&!X,Lt=Bt?t(v).addClass(Q).addClass(W.className).addClass(W.replacerClassName):t([]),Kt=Bt?Lt:_t,Vt=Lt.find(".sp-preview-inner"),$t=W.color||It&&_t.val(),Wt=!1,Xt=W.preferredFormat,Yt=!W.showButtons||W.clickoutFiresChange,Gt=!$t,Qt=W.allowEmpty&&!zt;d();var Jt={show:M,hide:F,toggle:A,reflow:z,option:L,enable:K,disable:V,offset:$,set:function(t){O(t),I()},get:q,destroy:B,container:kt};return Jt.id=p.push(Jt)-1,Jt}function s(e,r){var n=0,a=e.outerWidth(),i=e.outerHeight(),s=r.outerHeight(),o=e[0].ownerDocument,l=o.documentElement,c=l.clientWidth+t(o).scrollLeft(),f=l.clientHeight+t(o).scrollTop(),u=r.offset();return u.top+=s,u.left-=Math.min(u.left,u.left+a>c&&c>a?Math.abs(u.left+a-c):0),u.top-=Math.min(u.top,u.top+i>f&&f>i?Math.abs(i+s-n):n),u}function o(){}function l(t){t.stopPropagation()}function c(t,e){var r=Array.prototype.slice,n=r.call(arguments,2);return function(){return t.apply(e,n.concat(r.call(arguments)))}}function f(e,r,n,a){function i(t){t.stopPropagation&&t.stopPropagation(),t.preventDefault&&t.preventDefault(),t.returnValue=!1}function s(t){if(f){if(g&&c.documentMode<9&&!t.button)return l();var n=t.originalEvent&&t.originalEvent.touches&&t.originalEvent.touches[0],a=n&&n.pageX||t.pageX,s=n&&n.pageY||t.pageY,o=Math.max(0,Math.min(a-u.left,d)),b=Math.max(0,Math.min(s-u.top,h));p&&i(t),r.apply(e,[o,b,t])}}function o(r){var a=r.which?3==r.which:2==r.button;a||f||n.apply(e,arguments)!==!1&&(f=!0,h=t(e).height(),d=t(e).width(),u=t(e).offset(),t(c).bind(b),t(c.body).addClass("sp-dragging"),s(r),i(r))}function l(){f&&(t(c).unbind(b),t(c.body).removeClass("sp-dragging"),setTimeout(function(){a.apply(e,arguments)},0)),f=!1}r=r||function(){},n=n||function(){},a=a||function(){};var c=document,f=!1,u={},h=0,d=0,p="ontouchstart"in window,b={};b.selectstart=i,b.dragstart=i,b["touchmove mousemove"]=s,b["touchend mouseup"]=l,t(e).bind("touchstart mousedown",o)}function u(t,e,r){var n;return function(){var a=this,i=arguments,s=function(){n=null,t.apply(a,i)};r&&clearTimeout(n),(r||!n)&&(n=setTimeout(s,e))}}function h(){return t.fn.spectrum.inputTypeColorSupport()}var d={beforeShow:o,move:o,change:o,show:o,hide:o,color:!1,flat:!1,showInput:!1,allowEmpty:!1,showButtons:!0,clickoutFiresChange:!0,showInitial:!1,showPalette:!1,showPaletteOnly:!1,hideAfterPaletteSelect:!1,togglePaletteOnly:!1,showSelectionPalette:!0,localStorageKey:!1,appendTo:"body",maxSelectionSize:7,cancelText:"cancel",chooseText:"choose",togglePaletteMoreText:"more",togglePaletteLessText:"less",clearText:"Clear Color Selection",noColorSelectedText:"No Color Selected",preferredFormat:!1,className:"",containerClassName:"",replacerClassName:"",showAlpha:!1,theme:"sp-light",palette:[["#ffffff","#000000","#ff0000","#ff8000","#ffff00","#008000","#0000ff","#4b0082","#9400d3"]],selectionPalette:[],disabled:!1,offset:null},p=[],g=!!/msie/i.exec(window.navigator.userAgent),b=function(){function t(t,e){return!!~(""+t).indexOf(e)}var e=document.createElement("div"),r=e.style;return r.cssText="background-color:rgba(0,0,0,.5)",t(r.backgroundColor,"rgba")||t(r.backgroundColor,"hsla")}(),v=["<div class='sp-replacer'>","<div class='sp-preview'><div class='sp-preview-inner'></div></div>","<div class='sp-dd'>&#9660;</div>","</div>"].join(""),m=function(){var t="";if(g)for(var e=1;6>=e;e++)t+="<div class='sp-"+e+"'></div>";return["<div class='sp-container sp-hidden'>","<div class='sp-palette-container'>","<div class='sp-palette sp-thumb sp-cf'></div>","<div class='sp-palette-button-container sp-cf'>","<button type='button' class='sp-palette-toggle'></button>","</div>","</div>","<div class='sp-picker-container'>","<div class='sp-top sp-cf'>","<div class='sp-fill'></div>","<div class='sp-top-inner'>","<div class='sp-color'>","<div class='sp-sat'>","<div class='sp-val'>","<div class='sp-dragger'></div>","</div>","</div>","</div>","<div class='sp-clear sp-clear-display'>","</div>","<div class='sp-hue'>","<div class='sp-slider'></div>",t,"</div>","</div>","<div class='sp-alpha'><div class='sp-alpha-inner'><div class='sp-alpha-handle'></div></div></div>","</div>","<div class='sp-input-container sp-cf'>","<input class='sp-input' type='text' spellcheck='false' />","</div>","<div class='sp-initial sp-thumb sp-cf'></div>","<div class='sp-button-container sp-cf'>","<a class='sp-cancel' href='#'></a>","<button type='button' class='sp-choose'></button>","</div>","</div>","</div>"].join("")}(),y="spectrum.id";t.fn.spectrum=function(e,r){if("string"==typeof e){var n=this,a=Array.prototype.slice.call(arguments,1);return this.each(function(){var r=p[t(this).data(y)];if(r){var i=r[e];if(!i)throw new Error("Spectrum: no such method: '"+e+"'");"get"==e?n=r.get():"container"==e?n=r.container:"option"==e?n=r.option.apply(r,a):"destroy"==e?(r.destroy(),t(this).removeData(y)):i.apply(r,a)}}),n}return this.spectrum("destroy").each(function(){var r=t.extend({},e,t(this).data()),n=i(this,r);t(this).data(y,n.id)})},t.fn.spectrum.load=!0,t.fn.spectrum.loadOpts={},t.fn.spectrum.draggable=f,t.fn.spectrum.defaults=d,t.fn.spectrum.inputTypeColorSupport=function w(){if("undefined"==typeof w._cachedResult){var e=t("<input type='color'/>")[0];w._cachedResult="color"===e.type&&""!==e.value}return w._cachedResult},t.spectrum={},t.spectrum.localization={},t.spectrum.palettes={},t.fn.spectrum.processNativeColorInputs=function(){var e=t("input[type=color]");e.length&&!h()&&e.spectrum({preferredFormat:"hex6"})},function(){function t(t){var r={r:0,g:0,b:0},a=1,s=!1,o=!1;return"string"==typeof t&&(t=T(t)),"object"==typeof t&&(t.hasOwnProperty("r")&&t.hasOwnProperty("g")&&t.hasOwnProperty("b")?(r=e(t.r,t.g,t.b),s=!0,o="%"===String(t.r).substr(-1)?"prgb":"rgb"):t.hasOwnProperty("h")&&t.hasOwnProperty("s")&&t.hasOwnProperty("v")?(t.s=R(t.s),t.v=R(t.v),r=i(t.h,t.s,t.v),s=!0,o="hsv"):t.hasOwnProperty("h")&&t.hasOwnProperty("s")&&t.hasOwnProperty("l")&&(t.s=R(t.s),t.l=R(t.l),r=n(t.h,t.s,t.l),s=!0,o="hsl"),t.hasOwnProperty("a")&&(a=t.a)),a=x(a),{ok:s,format:t.format||o,r:D(255,I(r.r,0)),g:D(255,I(r.g,0)),b:D(255,I(r.b,0)),a:a}}function e(t,e,r){return{r:255*k(t,255),g:255*k(e,255),b:255*k(r,255)}}function r(t,e,r){t=k(t,255),e=k(e,255),r=k(r,255);var n,a,i=I(t,e,r),s=D(t,e,r),o=(i+s)/2;if(i==s)n=a=0;else{var l=i-s;switch(a=o>.5?l/(2-i-s):l/(i+s),i){case t:n=(e-r)/l+(r>e?6:0);break;case e:n=(r-t)/l+2;break;case r:n=(t-e)/l+4}n/=6}return{h:n,s:a,l:o}}function n(t,e,r){function n(t,e,r){return 0>r&&(r+=1),r>1&&(r-=1),1/6>r?t+6*(e-t)*r:.5>r?e:2/3>r?t+(e-t)*(2/3-r)*6:t}var a,i,s;if(t=k(t,360),e=k(e,100),r=k(r,100),0===e)a=i=s=r;else{var o=.5>r?r*(1+e):r+e-r*e,l=2*r-o;a=n(l,o,t+1/3),i=n(l,o,t),s=n(l,o,t-1/3)}return{r:255*a,g:255*i,b:255*s}}function a(t,e,r){t=k(t,255),e=k(e,255),r=k(r,255);var n,a,i=I(t,e,r),s=D(t,e,r),o=i,l=i-s;if(a=0===i?0:l/i,i==s)n=0;else{switch(i){case t:n=(e-r)/l+(r>e?6:0);break;case e:n=(r-t)/l+2;break;case r:n=(t-e)/l+4}n/=6}return{h:n,s:a,v:o}}function i(t,e,r){t=6*k(t,360),e=k(e,100),r=k(r,100);var n=j.floor(t),a=t-n,i=r*(1-e),s=r*(1-a*e),o=r*(1-(1-a)*e),l=n%6,c=[r,s,i,i,o,r][l],f=[o,r,r,s,i,i][l],u=[i,i,o,r,r,s][l];return{r:255*c,g:255*f,b:255*u}}function s(t,e,r,n){var a=[M(E(t).toString(16)),M(E(e).toString(16)),M(E(r).toString(16))];return n&&a[0].charAt(0)==a[0].charAt(1)&&a[1].charAt(0)==a[1].charAt(1)&&a[2].charAt(0)==a[2].charAt(1)?a[0].charAt(0)+a[1].charAt(0)+a[2].charAt(0):a.join("")}function o(t,e,r,n){var a=[M(H(n)),M(E(t).toString(16)),M(E(e).toString(16)),M(E(r).toString(16))];return a.join("")}function l(t,e){e=0===e?0:e||10;var r=B(t).toHsl();return r.s-=e/100,r.s=S(r.s),B(r)}function c(t,e){e=0===e?0:e||10;var r=B(t).toHsl();return r.s+=e/100,r.s=S(r.s),B(r)}function f(t){return B(t).desaturate(100)}function u(t,e){e=0===e?0:e||10;var r=B(t).toHsl();return r.l+=e/100,r.l=S(r.l),B(r)}function h(t,e){e=0===e?0:e||10;var r=B(t).toRgb();return r.r=I(0,D(255,r.r-E(255*-(e/100)))),r.g=I(0,D(255,r.g-E(255*-(e/100)))),r.b=I(0,D(255,r.b-E(255*-(e/100)))),B(r)}function d(t,e){e=0===e?0:e||10;var r=B(t).toHsl();return r.l-=e/100,r.l=S(r.l),B(r)}function p(t,e){var r=B(t).toHsl(),n=(E(r.h)+e)%360;return r.h=0>n?360+n:n,B(r)}function g(t){var e=B(t).toHsl();return e.h=(e.h+180)%360,B(e)}function b(t){var e=B(t).toHsl(),r=e.h;return[B(t),B({h:(r+120)%360,s:e.s,l:e.l}),B({h:(r+240)%360,s:e.s,l:e.l})]}function v(t){var e=B(t).toHsl(),r=e.h;return[B(t),B({h:(r+90)%360,s:e.s,l:e.l}),B({h:(r+180)%360,s:e.s,l:e.l}),B({h:(r+270)%360,s:e.s,l:e.l})]}function m(t){var e=B(t).toHsl(),r=e.h;return[B(t),B({h:(r+72)%360,s:e.s,l:e.l}),B({h:(r+216)%360,s:e.s,l:e.l})]}function y(t,e,r){e=e||6,r=r||30;var n=B(t).toHsl(),a=360/r,i=[B(t)];for(n.h=(n.h-(a*e>>1)+720)%360;--e;)n.h=(n.h+a)%360,i.push(B(n));return i}function w(t,e){e=e||6;for(var r=B(t).toHsv(),n=r.h,a=r.s,i=r.v,s=[],o=1/e;e--;)s.push(B({h:n,s:a,v:i})),i=(i+o)%1;return s}function _(t){var e={};for(var r in t)t.hasOwnProperty(r)&&(e[t[r]]=r);return e}function x(t){return t=parseFloat(t),(isNaN(t)||0>t||t>1)&&(t=1),t}function k(t,e){P(t)&&(t="100%");var r=A(t);return t=D(e,I(0,parseFloat(t))),r&&(t=parseInt(t*e,10)/100),j.abs(t-e)<1e-6?1:t%e/parseFloat(e)}function S(t){return D(1,I(0,t))}function C(t){return parseInt(t,16)}function P(t){return"string"==typeof t&&-1!=t.indexOf(".")&&1===parseFloat(t)}function A(t){return"string"==typeof t&&-1!=t.indexOf("%")}function M(t){return 1==t.length?"0"+t:""+t}function R(t){return 1>=t&&(t=100*t+"%"),t}function H(t){return Math.round(255*parseFloat(t)).toString(16)}function F(t){return C(t)/255}function T(t){t=t.replace(O,"").replace(q,"").toLowerCase();var e=!1;if(L[t])t=L[t],e=!0;else if("transparent"==t)return{r:0,g:0,b:0,a:0,format:"name"};var r;return(r=V.rgb.exec(t))?{r:r[1],g:r[2],b:r[3]}:(r=V.rgba.exec(t))?{r:r[1],g:r[2],b:r[3],a:r[4]}:(r=V.hsl.exec(t))?{h:r[1],s:r[2],l:r[3]}:(r=V.hsla.exec(t))?{h:r[1],s:r[2],l:r[3],a:r[4]}:(r=V.hsv.exec(t))?{h:r[1],s:r[2],v:r[3]}:(r=V.hsva.exec(t))?{h:r[1],s:r[2],v:r[3],a:r[4]}:(r=V.hex8.exec(t))?{a:F(r[1]),r:C(r[2]),g:C(r[3]),b:C(r[4]),format:e?"name":"hex8"}:(r=V.hex6.exec(t))?{r:C(r[1]),g:C(r[2]),b:C(r[3]),format:e?"name":"hex"}:(r=V.hex3.exec(t))?{r:C(r[1]+""+r[1]),g:C(r[2]+""+r[2]),b:C(r[3]+""+r[3]),format:e?"name":"hex"}:!1}var O=/^[\s,#]+/,q=/\s+$/,N=0,j=Math,E=j.round,D=j.min,I=j.max,z=j.random,B=function(e,r){if(e=e?e:"",r=r||{},e instanceof B)return e;if(!(this instanceof B))return new B(e,r);var n=t(e);this._originalInput=e,this._r=n.r,this._g=n.g,this._b=n.b,this._a=n.a,this._roundA=E(100*this._a)/100,this._format=r.format||n.format,this._gradientType=r.gradientType,this._r<1&&(this._r=E(this._r)),this._g<1&&(this._g=E(this._g)),this._b<1&&(this._b=E(this._b)),this._ok=n.ok,this._tc_id=N++};B.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},setAlpha:function(t){return this._a=x(t),this._roundA=E(100*this._a)/100,this},toHsv:function(){var t=a(this._r,this._g,this._b);return{h:360*t.h,s:t.s,v:t.v,a:this._a}},toHsvString:function(){var t=a(this._r,this._g,this._b),e=E(360*t.h),r=E(100*t.s),n=E(100*t.v);return 1==this._a?"hsv("+e+", "+r+"%, "+n+"%)":"hsva("+e+", "+r+"%, "+n+"%, "+this._roundA+")"},toHsl:function(){var t=r(this._r,this._g,this._b);return{h:360*t.h,s:t.s,l:t.l,a:this._a}},toHslString:function(){var t=r(this._r,this._g,this._b),e=E(360*t.h),n=E(100*t.s),a=E(100*t.l);return 1==this._a?"hsl("+e+", "+n+"%, "+a+"%)":"hsla("+e+", "+n+"%, "+a+"%, "+this._roundA+")"},toHex:function(t){return s(this._r,this._g,this._b,t)},toHexString:function(t){return"#"+this.toHex(t)},toHex8:function(){return o(this._r,this._g,this._b,this._a)},toHex8String:function(){return"#"+this.toHex8()},toRgb:function(){return{r:E(this._r),g:E(this._g),b:E(this._b),a:this._a}},toRgbString:function(){return 1==this._a?"rgb("+E(this._r)+", "+E(this._g)+", "+E(this._b)+")":"rgba("+E(this._r)+", "+E(this._g)+", "+E(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:E(100*k(this._r,255))+"%",g:E(100*k(this._g,255))+"%",b:E(100*k(this._b,255))+"%",a:this._a}},toPercentageRgbString:function(){return 1==this._a?"rgb("+E(100*k(this._r,255))+"%, "+E(100*k(this._g,255))+"%, "+E(100*k(this._b,255))+"%)":"rgba("+E(100*k(this._r,255))+"%, "+E(100*k(this._g,255))+"%, "+E(100*k(this._b,255))+"%, "+this._roundA+")"},toName:function(){return 0===this._a?"transparent":this._a<1?!1:K[s(this._r,this._g,this._b,!0)]||!1},toFilter:function(t){var e="#"+o(this._r,this._g,this._b,this._a),r=e,n=this._gradientType?"GradientType = 1, ":"";if(t){var a=B(t);r=a.toHex8String()}return"progid:DXImageTransform.Microsoft.gradient("+n+"startColorstr="+e+",endColorstr="+r+")"},toString:function(t){var e=!!t;t=t||this._format;var r=!1,n=this._a<1&&this._a>=0,a=!e&&n&&("hex"===t||"hex6"===t||"hex3"===t||"name"===t);return a?"name"===t&&0===this._a?this.toName():this.toRgbString():("rgb"===t&&(r=this.toRgbString()),"prgb"===t&&(r=this.toPercentageRgbString()),("hex"===t||"hex6"===t)&&(r=this.toHexString()),"hex3"===t&&(r=this.toHexString(!0)),"hex8"===t&&(r=this.toHex8String()),"name"===t&&(r=this.toName()),"hsl"===t&&(r=this.toHslString()),"hsv"===t&&(r=this.toHsvString()),r||this.toHexString())},_applyModification:function(t,e){var r=t.apply(null,[this].concat([].slice.call(e)));return this._r=r._r,this._g=r._g,this._b=r._b,this.setAlpha(r._a),this},lighten:function(){return this._applyModification(u,arguments)},brighten:function(){return this._applyModification(h,arguments)},darken:function(){return this._applyModification(d,arguments)},desaturate:function(){return this._applyModification(l,arguments)},saturate:function(){return this._applyModification(c,arguments)},greyscale:function(){return this._applyModification(f,arguments)},spin:function(){return this._applyModification(p,arguments)},_applyCombination:function(t,e){return t.apply(null,[this].concat([].slice.call(e)))},analogous:function(){return this._applyCombination(y,arguments)},complement:function(){return this._applyCombination(g,arguments)},monochromatic:function(){return this._applyCombination(w,arguments)},splitcomplement:function(){return this._applyCombination(m,arguments)},triad:function(){return this._applyCombination(b,arguments)},tetrad:function(){return this._applyCombination(v,arguments)}},B.fromRatio=function(t,e){if("object"==typeof t){var r={};for(var n in t)t.hasOwnProperty(n)&&("a"===n?r[n]=t[n]:r[n]=R(t[n]));t=r}return B(t,e)},B.equals=function(t,e){return t&&e?B(t).toRgbString()==B(e).toRgbString():!1},B.random=function(){return B.fromRatio({r:z(),g:z(),b:z()})},B.mix=function(t,e,r){r=0===r?0:r||50;var n,a=B(t).toRgb(),i=B(e).toRgb(),s=r/100,o=2*s-1,l=i.a-a.a;n=o*l==-1?o:(o+l)/(1+o*l),n=(n+1)/2;var c=1-n,f={r:i.r*n+a.r*c,g:i.g*n+a.g*c,b:i.b*n+a.b*c,a:i.a*s+a.a*(1-s)};return B(f)},B.readability=function(t,e){var r=B(t),n=B(e),a=r.toRgb(),i=n.toRgb(),s=r.getBrightness(),o=n.getBrightness(),l=Math.max(a.r,i.r)-Math.min(a.r,i.r)+Math.max(a.g,i.g)-Math.min(a.g,i.g)+Math.max(a.b,i.b)-Math.min(a.b,i.b);return{brightness:Math.abs(s-o),color:l}},B.isReadable=function(t,e){var r=B.readability(t,e);return r.brightness>125&&r.color>500},B.mostReadable=function(t,e){for(var r=null,n=0,a=!1,i=0;i<e.length;i++){var s=B.readability(t,e[i]),o=s.brightness>125&&s.color>500,l=3*(s.brightness/125)+s.color/500;(o&&!a||o&&a&&l>n||!o&&!a&&l>n)&&(a=o,n=l,r=B(e[i]))}return r};var L=B.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},K=B.hexNames=_(L),V=function(){var t="[-\\+]?\\d+%?",e="[-\\+]?\\d*\\.\\d+%?",r="(?:"+e+")|(?:"+t+")",n="[\\s|\\(]+("+r+")[,|\\s]+("+r+")[,|\\s]+("+r+")\\s*\\)?",a="[\\s|\\(]+("+r+")[,|\\s]+("+r+")[,|\\s]+("+r+")[,|\\s]+("+r+")\\s*\\)?";return{rgb:new RegExp("rgb"+n),rgba:new RegExp("rgba"+a),hsl:new RegExp("hsl"+n),hsla:new RegExp("hsla"+a),hsv:new RegExp("hsv"+n),hsva:new RegExp("hsva"+a),hex3:/^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex8:/^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/}}();window.tinycolor=B}(),t(function(){t.fn.spectrum.load&&t.fn.spectrum.processNativeColorInputs()})});
media/js/mce-timeTable-buttons.js ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*global tinymce:false, wp:false, console: false, md5:false, jBox:false, _:false, CommonManager:false, PopupEvents:false,Registry:false*/
2
+ (function($) {
3
+ "use strict";
4
+
5
+ tinymce.PluginManager.add('mp_timetable', function(editor, url) {
6
+ editor.addButton('addTimeTableButton', {
7
+ title: 'TimeTable',
8
+ icon: false,
9
+ //text: 'Add Time Table',
10
+ image: url + '/../img/shortcode-icon.png',
11
+ onclick: function() {
12
+
13
+ Registry._get("adminFunctions").callModal("", function(container) {
14
+ //callback open
15
+ var jbox = this;
16
+
17
+ Registry._get("adminFunctions").wpAjax(
18
+ {
19
+ controller: "popup",
20
+ action: "get_popup_html_content"
21
+ },
22
+ function(data) {
23
+ jbox.setContent(data.html);
24
+ //click on insert button
25
+ Registry._get("adminFunctions").initJBox(jbox, container, function(item) {
26
+
27
+ var shortcode_obj = {
28
+ tag: 'mp-timetable',
29
+ attrs: {},
30
+ type: "single"
31
+ };
32
+
33
+ $.each(item, function(index, parameters) {
34
+ switch (parameters.name) {
35
+ case 'event':
36
+ if (shortcode_obj.attrs.events === '' || typeof shortcode_obj.attrs.events === 'undefined') {
37
+ shortcode_obj.attrs.events = parameters.value;
38
+ } else {
39
+ shortcode_obj.attrs.events += ',' + parameters.value;
40
+ }
41
+ break;
42
+ case 'event_category':
43
+ if (shortcode_obj.attrs.event_categ === '' || typeof shortcode_obj.attrs.event_categ === 'undefined') {
44
+ shortcode_obj.attrs.event_categ = parameters.value;
45
+ } else {
46
+ shortcode_obj.attrs.event_categ += ',' + parameters.value;
47
+ }
48
+ break;
49
+ case 'weekday':
50
+ if (shortcode_obj.attrs.col === '' || typeof shortcode_obj.attrs.col === 'undefined') {
51
+ shortcode_obj.attrs.col = parameters.value;
52
+ } else {
53
+ shortcode_obj.attrs.col += ',' + parameters.value;
54
+ }
55
+ break;
56
+ case 'measure':
57
+ shortcode_obj.attrs.increment = parameters.value;
58
+ break;
59
+ case 'filter_style':
60
+ shortcode_obj.attrs.view = parameters.value;
61
+ break;
62
+ case 'filter_label':
63
+ shortcode_obj.attrs.label = parameters.value;
64
+ break;
65
+ case 'hide_all_events_view':
66
+ shortcode_obj.attrs.hide_label = parameters.value;
67
+ break;
68
+ case 'hide_hours_column':
69
+ shortcode_obj.attrs.hide_hrs = parameters.value;
70
+ break;
71
+ case 'group_events':
72
+ shortcode_obj.attrs.group = parameters.value;
73
+ break;
74
+ case 'hide_empty':
75
+ shortcode_obj.attrs.hide_empty_rows = parameters.value;
76
+ break;
77
+ case 'title':
78
+ shortcode_obj.attrs.title = parameters.value;
79
+ break;
80
+ case 'time':
81
+ shortcode_obj.attrs.time = parameters.value;
82
+ break;
83
+ case 'sub-title':
84
+ shortcode_obj.attrs['sub-title'] = parameters.value;
85
+ break;
86
+ case 'description':
87
+ shortcode_obj.attrs.description = parameters.value;
88
+ break;
89
+ case 'user':
90
+ shortcode_obj.attrs.user = parameters.value;
91
+ break;
92
+ case 'disable_event_url':
93
+ shortcode_obj.attrs.disable_event_url = parameters.value;
94
+ break;
95
+ case 'text_align':
96
+ shortcode_obj.attrs.text_align = parameters.value;
97
+ break;
98
+ case 'text_align_vertical':
99
+ shortcode_obj.attrs.text_align_vertical = parameters.value;
100
+ break;
101
+ case 'id':
102
+ shortcode_obj.attrs.id = parameters.value;
103
+ break;
104
+ case 'custom_class':
105
+ shortcode_obj.attrs.custom_class = parameters.value;
106
+ break;
107
+ case 'row_height':
108
+ shortcode_obj.attrs.row_height = parameters.value;
109
+ break;
110
+ case 'font_size':
111
+ shortcode_obj.attrs.font_size = parameters.value;
112
+ break;
113
+ case 'responsive':
114
+ shortcode_obj.attrs.responsive = parameters.value;
115
+ break;
116
+ }
117
+ });
118
+
119
+ var shortcode = wp.shortcode.string(shortcode_obj);
120
+
121
+ editor.insertContent(shortcode);
122
+
123
+ jbox.close();
124
+
125
+ });
126
+ },
127
+ function(data) {
128
+ console.warn(data);
129
+ }
130
+ );
131
+ }
132
+ );
133
+ }
134
+ });
135
+ });
136
+ })(window.jQuery);
media/js/mce-timeTable-buttons.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t){"use strict";tinymce.PluginManager.add("mp_timetable",function(e,n){e.addButton("addTimeTableButton",{title:"TimeTable",icon:!1,image:n+"/../img/shortcode-icon.png",onclick:function(){Registry._get("adminFunctions").callModal("",function(n){var i=this;Registry._get("adminFunctions").wpAjax({controller:"popup",action:"get_popup_html_content"},function(a){i.setContent(a.html),Registry._get("adminFunctions").initJBox(i,n,function(n){var a={tag:"mp-timetable",attrs:{},type:"single"};t.each(n,function(t,e){switch(e.name){case"event":""===a.attrs.events||"undefined"==typeof a.attrs.events?a.attrs.events=e.value:a.attrs.events+=","+e.value;break;case"event_category":""===a.attrs.event_categ||"undefined"==typeof a.attrs.event_categ?a.attrs.event_categ=e.value:a.attrs.event_categ+=","+e.value;break;case"weekday":""===a.attrs.col||"undefined"==typeof a.attrs.col?a.attrs.col=e.value:a.attrs.col+=","+e.value;break;case"measure":a.attrs.increment=e.value;break;case"filter_style":a.attrs.view=e.value;break;case"filter_label":a.attrs.label=e.value;break;case"hide_all_events_view":a.attrs.hide_label=e.value;break;case"hide_hours_column":a.attrs.hide_hrs=e.value;break;case"group_events":a.attrs.group=e.value;break;case"hide_empty":a.attrs.hide_empty_rows=e.value;break;case"title":a.attrs.title=e.value;break;case"time":a.attrs.time=e.value;break;case"sub-title":a.attrs["sub-title"]=e.value;break;case"description":a.attrs.description=e.value;break;case"user":a.attrs.user=e.value;break;case"disable_event_url":a.attrs.disable_event_url=e.value;break;case"text_align":a.attrs.text_align=e.value;break;case"text_align_vertical":a.attrs.text_align_vertical=e.value;break;case"id":a.attrs.id=e.value;break;case"custom_class":a.attrs.custom_class=e.value;break;case"row_height":a.attrs.row_height=e.value;break;case"font_size":a.attrs.font_size=e.value;break;case"responsive":a.attrs.responsive=e.value}});var s=wp.shortcode.string(a);e.insertContent(s),i.close()})},function(t){console.warn(t)})})}})})}(window.jQuery);
media/js/mptt-functions.js ADDED
@@ -0,0 +1,370 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* global console:false,$:false,jQuery:false, _:false, Registry:false, wp:false,pagenow:false,typenow:false, jBox:false */
2
+ window.Registry = (function() {
3
+ "use strict";
4
+ var modules = {};
5
+
6
+ /**
7
+ * Test module
8
+ * @param module
9
+ * @returns {boolean}
10
+ * @private
11
+ */
12
+ function _testModule(module) {
13
+ var status = false;
14
+
15
+ if (typeof module.getInstance === 'function') {
16
+ status = true;
17
+ }
18
+
19
+ return status;
20
+ }
21
+
22
+ /**
23
+ * Register module
24
+ * @param name
25
+ * @param module
26
+ */
27
+ function register(name, module) {
28
+ if (_testModule(module)) {
29
+ modules[name] = module;
30
+ } else {
31
+ throw new Error('Invalide module "' + name + '". The function "getInstance" is not defined.');
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Register modules
37
+ * @param map
38
+ */
39
+ function registryMap(map) {
40
+ for (var name in map) {
41
+ if (!map.hasOwnProperty(name)) {
42
+ continue;
43
+ }
44
+ if (_testModule(map[name])) {
45
+ modules[name] = map[name];
46
+ } else {
47
+ throw new Error('Invalide module "' + name + '" inside the collection. The function "getInstance" is not defined.');
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Unregister
54
+ * @param name
55
+ */
56
+ function unregister(name) {
57
+ delete modules[name];
58
+ }
59
+
60
+ /**
61
+ * Get instance module
62
+ * @param name
63
+ * @returns {*|wp.mce.View}
64
+ */
65
+ function _get(name) {
66
+ var module = modules[name];
67
+ if (!module) {
68
+ throw new Error('The module "' + name + '" has not been registered or it was unregistered.');
69
+ }
70
+
71
+ if (typeof module.getInstance !== 'function') {
72
+ throw new Error('The module "' + name + '" can not be instantiated. ' + 'The function "getInstance" is not defined.');
73
+ }
74
+
75
+ return modules[name].getInstance();
76
+ }
77
+
78
+ return {
79
+ register: register,
80
+ unregister: unregister,
81
+ _get: _get,
82
+ registryMap: registryMap
83
+ };
84
+ })();
85
+
86
+ Registry.register("adminFunctions", (function($) {
87
+ "use strict";
88
+
89
+ var state, $jBox, container;
90
+
91
+ function createInstance() {
92
+ return {
93
+ /**
94
+ * WP Ajax
95
+ *
96
+ * @param {object} params
97
+ * @param {function} callbackSuccess
98
+ * @param {function} callbackError
99
+ * @returns {undefined}
100
+ */
101
+ wpAjax: function(params, callbackSuccess, callbackError) {
102
+
103
+ params.mptt_action = params.action;
104
+
105
+ delete params.action;
106
+
107
+ wp.ajax.send("route_url", {
108
+
109
+ success: function(data) {
110
+ if (!_.isUndefined(callbackError) && _.isFunction(callbackError)) {
111
+ callbackSuccess(data);
112
+ }
113
+ },
114
+ error: function(data) {
115
+ if (!_.isUndefined(callbackError) && _.isFunction(callbackError)) {
116
+ callbackError(data);
117
+ } else {
118
+ console.log(data);
119
+ }
120
+ },
121
+ data: params
122
+ });
123
+ },
124
+ /**
125
+ * Init Jbox
126
+ *
127
+ * @param jbox
128
+ * @param html_container
129
+ * @param buttonCallback
130
+ */
131
+ initJBox: function(jbox, html_container, buttonCallback) {
132
+ $jBox = jbox;
133
+ container = html_container;
134
+ state.buttonEvents(buttonCallback);
135
+ },
136
+ /**
137
+ * Button save event
138
+ * @param buttonCallback
139
+ */
140
+ buttonEvents: function(buttonCallback) {
141
+ container.find("#insert-into").off("click").on("click", function() {
142
+ buttonCallback($(this).parents('form').serializeArray());
143
+ });
144
+ },
145
+ /**
146
+ * Open popup window function
147
+ *
148
+ * @param start_content
149
+ * @param open_callback
150
+ * @param args
151
+ */
152
+ callModal: function(start_content, open_callback, args) {
153
+ var height = $(window).outerHeight() - 60,
154
+ width = $(window).outerWidth() - 60,
155
+ spinner = wp.html.string({
156
+ 'tag': "span",
157
+ 'attrs': {
158
+ 'class': "spinner is-active"
159
+ },
160
+ 'content': ""
161
+ }
162
+ ),
163
+ params = {
164
+ content: start_content,
165
+ closeOnEsc: true,
166
+ animation: {open: 'zoomIn', close: 'zoomOut'},
167
+ width: width,
168
+ height: height,
169
+ closeButton: "title",
170
+ title: 'Shortcode Settings',
171
+ onOpen: function() {
172
+ var jbox_container = $("#" + this.id);
173
+
174
+ open_callback.call(this, jbox_container);
175
+ },
176
+ onClose: function() {
177
+ //Registry._get('ContentDML').nonceAjax = Math.random().toString(36).slice(2);
178
+ $("#" + this.id).remove();
179
+ }
180
+ };
181
+ if (!_.isUndefined(args)) {
182
+ $.extend(params, args);
183
+ }
184
+
185
+ var popup = new jBox('Modal', params);
186
+
187
+ popup.open();
188
+ },
189
+ /**
190
+ * Parse Url Request
191
+ *
192
+ * @param {type} value - get params name
193
+ */
194
+ parseRequest: function(value) {
195
+ var request = location.search;
196
+ var array,
197
+ result = {};
198
+ if (_.isEmpty(request) || request === "?") {
199
+ return result;
200
+ }
201
+ request = request.replace("?", "");
202
+ array = request.split("&");
203
+ $.each(array, function() {
204
+ var value = this;
205
+ value = value.split("=");
206
+ result[value[0]] = value[1];
207
+ });
208
+ if (_.isUndefined(value)) {
209
+ return result;
210
+ } else {
211
+ return result[value];
212
+ }
213
+
214
+ }, /**
215
+ * Generate HTML
216
+ *
217
+ * @param params - json
218
+ * @returns {string|*|n|string}
219
+ */
220
+ generateHTML: function(params) {
221
+ var content = "",
222
+ result;
223
+ if (_.isObject(params)) {
224
+ var element = document.createElement(params.tag);
225
+ if (!_.isUndefined(params.attrs)) {
226
+ $.each(params.attrs, function(key, value) {
227
+ if (!_.isUndefined(value) && value !== '') {
228
+ element.setAttribute(key, value);
229
+ }
230
+ });
231
+ }
232
+ if (_.isArray(params.content)) {
233
+
234
+ $.each(params.content, function(key, value) {
235
+ content += state.generateHTML(value);
236
+ });
237
+ $(element).html(content);
238
+ } else if (_.isObject(params.content)) {
239
+ content = state.generateHTML(params.content);
240
+ $(element).html(content);
241
+ } else {
242
+ if (!_.isUndefined(params.content)) {
243
+ $(element).html(params.content);
244
+ } else {
245
+ $(element).html("");
246
+ }
247
+ }
248
+ result = $(element).get(0).outerHTML;
249
+ } else if (_.isString(params)) {
250
+ result = params;
251
+ } else {
252
+ result = false;
253
+ }
254
+ return result;
255
+ },
256
+ /**
257
+ * Put the data to html code and return here
258
+ *
259
+ * @param $template
260
+ * @param $data
261
+ * @returns {boolean}
262
+ */
263
+ getHtml: function($template, $data) {
264
+ if (_.isUndefined($template)) {
265
+ return false;
266
+ }
267
+ var result = false;
268
+ if (_.isUndefined($data)) {
269
+ if (_.isArray($template)) {
270
+ result = "";
271
+ $.each($template, function(key, value) {
272
+ result += state.generateHTML(value);
273
+ });
274
+ } else {
275
+ result = state.generateHTML($template);
276
+ }
277
+ }
278
+ if (_.isObject($data)) {
279
+ var template = _.template(result);
280
+ result = template($data);
281
+ }
282
+ return result;
283
+ }
284
+ };
285
+ }
286
+
287
+ return {
288
+ getInstance: function() {
289
+ if (!state) {
290
+ state = createInstance();
291
+ }
292
+ return state;
293
+ }
294
+ };
295
+ })(jQuery));
296
+
297
+
298
+ (function($) {
299
+ "use strict";
300
+
301
+ $(document).ready(function() {
302
+
303
+ var mptt_table_init = function() {
304
+ var body = $('body');
305
+
306
+ if(detectIE()){
307
+ if(!body.hasClass('mprm_ie_browser')){
308
+ body.addClass('mprm_ie_browser');
309
+ }
310
+ }else{
311
+ body.removeClass('mprm_ie_browser');
312
+ }
313
+
314
+ var $mptt_shortcode_wrapper = $('.mptt-shortcode-wrapper');
315
+
316
+ if ((typeof typenow) !== "undefined") {
317
+ if (pagenow === typenow) {
318
+ switch (typenow) {
319
+ case 'mp-event':
320
+ Registry._get("Event").init();
321
+ break;
322
+ case 'mp-column':
323
+ Registry._get("Event").initDatePicker();
324
+ Registry._get("Event").columnRadioBox();
325
+ break;
326
+ default:
327
+ break;
328
+ }
329
+ }
330
+ }
331
+
332
+ if ($mptt_shortcode_wrapper.length) {
333
+ Registry._get("Event").initTableData();
334
+ Registry._get("Event").filterShortcodeEvents();
335
+ Registry._get("Event").getFilterByHash();
336
+
337
+ $mptt_shortcode_wrapper.show();
338
+ $mptt_shortcode_wrapper.addClass('table-init');
339
+ }
340
+
341
+ if ($('.upcoming-events-widget').length || $mptt_shortcode_wrapper.length) {
342
+ Registry._get("Event").setColorSettings();
343
+ }
344
+ }
345
+
346
+ window.mptt = {};
347
+
348
+ window.mptt.tableInit = mptt_table_init;
349
+ mptt_table_init();
350
+ });
351
+ })(jQuery);
352
+
353
+ function detectIE() {
354
+ var ua = window.navigator.userAgent;
355
+
356
+ var msie = ua.indexOf('MSIE ');
357
+ if (msie > 0) {
358
+ // IE 10 or older => return version number
359
+ return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
360
+ }
361
+
362
+ var trident = ua.indexOf('Trident/');
363
+ if (trident > 0) {
364
+ // IE 11 => return version number
365
+ var rv = ua.indexOf('rv:');
366
+ return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
367
+ }
368
+ // other browser
369
+ return false;
370
+ }
media/js/mptt-functions.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([,function(e,t){window.Registry=function(){"use strict";var e={};function t(e){var t=!1;return"function"==typeof e.getInstance&&(t=!0),t}return{register:function(n,i){if(!t(i))throw new Error('Invalide module "'+n+'". The function "getInstance" is not defined.');e[n]=i},unregister:function(t){delete e[t]},_get:function(t){var n=e[t];if(!n)throw new Error('The module "'+t+'" has not been registered or it was unregistered.');if("function"!=typeof n.getInstance)throw new Error('The module "'+t+'" can not be instantiated. The function "getInstance" is not defined.');return e[t].getInstance()},registryMap:function(n){for(var i in n)if(n.hasOwnProperty(i)){if(!t(n[i]))throw new Error('Invalide module "'+i+'" inside the collection. The function "getInstance" is not defined.');e[i]=n[i]}}}}(),Registry.register("adminFunctions",function(e){"use strict";var t,n;return{getInstance:function(){return t||(t={wpAjax:function(e,t,n){e.mptt_action=e.action,delete e.action,wp.ajax.send("route_url",{success:function(e){!_.isUndefined(n)&&_.isFunction(n)&&t(e)},error:function(e){!_.isUndefined(n)&&_.isFunction(n)?n(e):console.log(e)},data:e})},initJBox:function(e,i,r){e,n=i,t.buttonEvents(r)},buttonEvents:function(t){n.find("#insert-into").off("click").on("click",function(){t(e(this).parents("form").serializeArray())})},callModal:function(t,n,i){var r=e(window).outerHeight()-60,o=e(window).outerWidth()-60,s=(wp.html.string({tag:"span",attrs:{class:"spinner is-active"},content:""}),{content:t,closeOnEsc:!0,animation:{open:"zoomIn",close:"zoomOut"},width:o,height:r,closeButton:"title",title:"Shortcode Settings",onOpen:function(){var t=e("#"+this.id);n.call(this,t)},onClose:function(){e("#"+this.id).remove()}});_.isUndefined(i)||e.extend(s,i),new jBox("Modal",s).open()},parseRequest:function(t){var n,i=location.search,r={};return _.isEmpty(i)||"?"===i?r:(n=(i=i.replace("?","")).split("&"),e.each(n,function(){var e=this;e=e.split("="),r[e[0]]=e[1]}),_.isUndefined(t)?r:r[t])},generateHTML:function(n){var i,r="";if(_.isObject(n)){var o=document.createElement(n.tag);_.isUndefined(n.attrs)||e.each(n.attrs,function(e,t){_.isUndefined(t)||""===t||o.setAttribute(e,t)}),_.isArray(n.content)?(e.each(n.content,function(e,n){r+=t.generateHTML(n)}),e(o).html(r)):_.isObject(n.content)?(r=t.generateHTML(n.content),e(o).html(r)):_.isUndefined(n.content)?e(o).html(""):e(o).html(n.content),i=e(o).get(0).outerHTML}else i=!!_.isString(n)&&n;return i},getHtml:function(n,i){if(_.isUndefined(n))return!1;var r=!1;if(_.isUndefined(i)&&(_.isArray(n)?(r="",e.each(n,function(e,n){r+=t.generateHTML(n)})):r=t.generateHTML(n)),_.isObject(i)){var o=_.template(r);r=o(i)}return r}}),t}}}(jQuery)),function(e){"use strict";e(document).ready(function(){var t=function(){var t=e("body");!function(){var e=window.navigator.userAgent,t=e.indexOf("MSIE ");if(t>0)return parseInt(e.substring(t+5,e.indexOf(".",t)),10);if(e.indexOf("Trident/")>0){var n=e.indexOf("rv:");return parseInt(e.substring(n+3,e.indexOf(".",n)),10)}return!1}()?t.removeClass("mprm_ie_browser"):t.hasClass("mprm_ie_browser")||t.addClass("mprm_ie_browser");var n=e(".mptt-shortcode-wrapper");if("undefined"!=typeof typenow&&pagenow===typenow)switch(typenow){case"mp-event":Registry._get("Event").init();break;case"mp-column":Registry._get("Event").initDatePicker(),Registry._get("Event").columnRadioBox()}n.length&&(Registry._get("Event").initTableData(),Registry._get("Event").filterShortcodeEvents(),Registry._get("Event").getFilterByHash(),n.show(),n.addClass("table-init")),(e(".upcoming-events-widget").length||n.length)&&Registry._get("Event").setColorSettings()};window.mptt={},window.mptt.tableInit=t,t()})}(jQuery)}]);
media/less/admin.less ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #mp-event_data {
2
+ #add_event_table {
3
+ #event_start, #event_end {
4
+ min-width: 100px;
5
+ }
6
+ }
7
+ .spinner.left {
8
+ float: none;
9
+ }
10
+
11
+ .events-list-wrapper {
12
+ max-height: 360px;
13
+ overflow-y: auto;
14
+ overflow-x: hidden;
15
+ margin-bottom: 1em;
16
+ }
17
+ #events-list {
18
+
19
+ .active {
20
+ * {
21
+ background-color: #F7FCFE;
22
+ }
23
+ td:first-child {
24
+ border-left: 4px solid #0091cd;
25
+ }
26
+ }
27
+
28
+ .edit-event-button, .delete-event-button {
29
+ margin: 0 5px 10px 0;
30
+ position: relative;
31
+ width: 2em;
32
+ height: 2em;
33
+
34
+ &.icon:before {
35
+ font-family: Dashicons;
36
+ font-variant: normal;
37
+ font-weight: 400;
38
+ height: 100%;
39
+ left: 0;
40
+ line-height: 1.85;
41
+ margin: 0;
42
+ position: absolute;
43
+ text-align: center;
44
+ text-indent: 0;
45
+ text-transform: none;
46
+ top: 0;
47
+ width: 100%;
48
+ }
49
+ }
50
+ }
51
+ }
52
+
53
+ @media screen and (max-width: 1200px) {
54
+ #mp-event_data {
55
+ #events-list {
56
+ .event-description {
57
+ overflow: hidden;
58
+ text-overflow: ellipsis;
59
+ white-space: nowrap;
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ #column-options {
66
+ .mp-weekday, .mp-date {
67
+ margin: 10px 0 0;
68
+ }
69
+ }
70
+
71
+ div[id^="jBoxID"] {
72
+ &.jBox-Modal {
73
+ .jBox-container {
74
+ overflow: hidden;
75
+ }
76
+ .jBox-content {
77
+ max-width: 980px;
78
+ max-height: 800px;
79
+ padding: 0;
80
+ overflow: hidden;
81
+ & > form {
82
+ height: 100%;
83
+ overflow: hidden;
84
+ position: relative;
85
+ .mptt-shortcode-settings-wrapper {
86
+ height: ~"calc(100% - 60px)";
87
+ overflow-y: scroll;
88
+ padding: 0 15px 0 10px;
89
+ }
90
+ .mptt-shortcode-submit-wrapper {
91
+ background: #f4f5f6 none repeat scroll 0 0;
92
+ border-top: 1px solid #ddd;
93
+ padding: 15px;
94
+ position: relative;
95
+ text-align: left;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ #add_event_options_table, #add_event_table {
103
+ &.form-table {
104
+ tr {
105
+ > :first-child {
106
+ vertical-align: top;
107
+ text-align: left;
108
+ padding: 20px 10px 20px 0;
109
+ width: 200px;
110
+ line-height: 1.3;
111
+ font-weight: 600;
112
+ }
113
+ }
114
+ }
115
+ }
116
+
media/less/block-editor.less ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ .wp-block[data-type="mp-timetable/timetable"] div[data-block] {
2
+ overflow: auto;
3
+ }
media/less/no-js.less ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .mptt-shortcode-wrapper {
2
+ .mptt-shortcode-table {
3
+ &:first-of-type {
4
+ display: table !important;
5
+ }
6
+ .mptt-event-container {
7
+ &:hover {
8
+ height: auto !important;
9
+ min-height: 100% !important;
10
+ }
11
+ }
12
+ }
13
+ }
14
+ body {
15
+ &.mprm_ie_browser {
16
+ .mptt-shortcode-wrapper {
17
+ .mptt-event-container {
18
+ height: auto !important;
19
+ }
20
+ }
21
+ }
22
+ }
23
+ @media (max-width: 767px) {
24
+ .mptt-shortcode-wrapper {
25
+ .mptt-shortcode-table {
26
+ &:first-of-type {
27
+ display: none !important;
28
+ }
29
+ }
30
+ }
31
+ }
media/less/style.less ADDED
@@ -0,0 +1,463 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @default_color: #3b5998;
2
+
3
+ @import 'theme.less';
4
+ //@import "no-js";
5
+ .mptt-main-wrapper {
6
+ &.mp-event-item {
7
+ img:not(.avatar ) {
8
+ width: 100%;
9
+ height: auto;
10
+ }
11
+ }
12
+ .mptt-content {
13
+ width: 66.999%;
14
+ margin-right: 3%;
15
+ float: left;
16
+ }
17
+ .mptt-sidebar {
18
+ width: 30%;
19
+ float: left;
20
+ }
21
+
22
+ .event-title {
23
+ margin: 0 0 2rem;
24
+ }
25
+ .thumbnail-wrapper {
26
+ margin-bottom: 1rem;
27
+ }
28
+ .event-content {
29
+ margin: 1rem 0;
30
+ }
31
+ .timeslots-title {
32
+ margin: 1rem 0;
33
+ }
34
+ .mptt-event.events-list {
35
+ margin: 0 0 1rem 2rem;
36
+ list-style: none;
37
+ padding: 0;
38
+
39
+ .event {
40
+ list-style: outside;
41
+ padding: 1rem;
42
+
43
+ .event-title {
44
+ margin: 0.25rem 0 0;
45
+ padding: 0;
46
+ }
47
+ .event-subtitle {
48
+ margin: 0.25rem 0 0;
49
+ padding: 0;
50
+ }
51
+ .timeslot,
52
+ .event-user {
53
+ margin: 0.25rem 0 0;
54
+ padding: 0;
55
+ }
56
+ .event-description {
57
+ margin: 0.25rem 0 0;
58
+ padding: 0;
59
+ hyphens: auto;
60
+ }
61
+ .event-user {
62
+ .avatar {
63
+ border-radius: 50%;
64
+ }
65
+ }
66
+ }
67
+ .event:last-child {
68
+ margin-bottom: 0;
69
+ }
70
+ }
71
+
72
+ .column-title {
73
+ margin: 0 0 2rem 0;
74
+ }
75
+ .column-content {
76
+ margin: 1rem 0;
77
+ }
78
+ .mptt-column.events-list {
79
+ margin: 0 0 2rem 0;
80
+ list-style: none;
81
+ padding: 0;
82
+ .event {
83
+ list-style: none;
84
+ padding: 1rem;
85
+
86
+ .event-thumbnail {
87
+ float: left;
88
+ margin: 0 2rem 2rem 0;
89
+ }
90
+ .event-title {
91
+ clear: none;
92
+ margin: 0 0 0.25rem 0;
93
+ padding: 0;
94
+ }
95
+ .event-subtitle {
96
+ margin: 0.25rem 0 0;
97
+ padding: 0;
98
+ }
99
+ .timeslot,
100
+ .event-user {
101
+ margin: 0.25rem 0 0;
102
+ padding: 0;
103
+ }
104
+ .event-description {
105
+ margin: 0.25rem 0 0;
106
+ padding: 0;
107
+ hyphens: auto;
108
+ }
109
+ .event-user {
110
+ .avatar {
111
+ border-radius: 50%;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ .upcoming-events-widget {
119
+ margin-top: 2rem;
120
+ .mptt-widget.events-list {
121
+ list-style: none;
122
+ margin: 0;
123
+ padding: 0;
124
+ .event {
125
+ list-style: none;
126
+ margin-bottom: 2rem;
127
+ padding: 0.5rem 2rem;
128
+ border: none;
129
+ border-left: 3px solid @default_color;
130
+
131
+ .event-title {
132
+ margin: 0 0 0.25rem 0;
133
+ padding: 0;
134
+ }
135
+ .column-title {
136
+ margin: 0.25rem 0 0;
137
+ padding: 0;
138
+ }
139
+ .timeslot,
140
+ .event-user {
141
+ margin: 0.25rem 0 0;
142
+ padding: 0;
143
+ }
144
+ &.mptt-colorized {
145
+ .event-title {
146
+ margin: 0 0 0.25rem 0;
147
+ padding: 0;
148
+ color: inherit;
149
+
150
+ .event-link {
151
+ color: inherit;
152
+ }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ .mptt-hidden {
160
+ display: none;
161
+ opacity: 0;
162
+ clear: both;
163
+ }
164
+
165
+ .mprm_ie_browser {
166
+ .mptt-inner-event-content {
167
+ width: 100%;
168
+ }
169
+ }
170
+
171
+ .mptt-container {
172
+ list-style: none;
173
+ }
174
+
175
+ /*====== Widget End ======*/
176
+
177
+ /* ====== Shortcode start ====== */
178
+ .mptt-shortcode-wrapper {
179
+ .mptt-shortcode-event {
180
+ .mptt-event-container {
181
+ display: flex;
182
+ flex-direction: column;
183
+ &:hover {
184
+ //min-height: 100%;
185
+ display: flex;
186
+ }
187
+ }
188
+
189
+ &.mptt-event-vertical-top {
190
+ .mptt-event-container {
191
+ justify-content: flex-start;
192
+ align-content: flex-start;
193
+ }
194
+ }
195
+ &.mptt-event-vertical-middle {
196
+ .mptt-event-container {
197
+ justify-content: center;
198
+ align-content: center;
199
+ }
200
+ }
201
+ &.mptt-event-vertical-bottom {
202
+ .mptt-event-container {
203
+ justify-content: flex-end;
204
+ align-content: flex-end;
205
+ }
206
+ }
207
+ }
208
+
209
+ .mptt-shortcode-table {
210
+ margin-top: 1rem;
211
+ tr.mptt-shortcode-row {
212
+ th {
213
+ background-color: #f0f0f0;
214
+ border: 1px solid #ffffff;
215
+ text-align: center;
216
+ color: #404040;
217
+ padding: 0.25rem 1rem;
218
+ overflow-wrap: normal;
219
+ hyphens: none;
220
+ }
221
+
222
+ }
223
+ tbody {
224
+ tr:nth-child(2n+2) {
225
+ background-color: #f0f0f0;
226
+ }
227
+ td {
228
+ position: relative;
229
+ border: 1px solid #ffffff;
230
+ padding: 0;
231
+ line-height: normal;
232
+ box-sizing: initial;
233
+ &.event {
234
+ background-color: @default_color;
235
+ }
236
+ &.mptt-shortcode-hours {
237
+ padding: 0 1rem !important;
238
+ white-space: nowrap;
239
+ width: 1%;
240
+ vertical-align: middle;
241
+ }
242
+ }
243
+ .mptt-event-container {
244
+ position: absolute;
245
+ top: 0;
246
+ right: 0;
247
+ left: 0;
248
+ z-index: 3;
249
+ overflow: hidden;
250
+ outline: 1px solid #fff;
251
+ color: inherit;
252
+ /*word-break: break-all;
253
+ word-wrap: break-word;*/
254
+
255
+ &:hover {
256
+ background-color: inherit;
257
+ overflow: visible;
258
+ z-index: 10;
259
+ .event-title {
260
+ text-decoration: underline;
261
+ }
262
+ }
263
+
264
+ p {
265
+ color: inherit;
266
+ }
267
+
268
+ .event-title {
269
+ color: inherit;
270
+ display: inline-block;
271
+ margin: 0.5rem 0.25rem 0.25rem 0.25rem;
272
+ text-decoration: none;
273
+ font-size: 1.1em;
274
+ //word-break: break-all;
275
+ }
276
+ .event-subtitle {
277
+ margin: 0.25rem;
278
+ font-size: 0.8em;
279
+ }
280
+ .event-user {
281
+ margin: 0.25rem;
282
+ font-size: 0.8em;
283
+ .avatar {
284
+ border-radius: 50%;
285
+ margin-right: 3px;
286
+ display: inline-block;
287
+ vertical-align: middle;
288
+ }
289
+ }
290
+ .event-description {
291
+ font-size: 0.8em;
292
+ margin: 0.25rem;
293
+ hyphens: auto;
294
+ }
295
+ .timeslot {
296
+ font-size: 0.9em;
297
+ margin: 0.25rem;
298
+ }
299
+ }
300
+ }
301
+
302
+ &.mptt-theme-mode tbody {
303
+ tr:nth-child(2n+2) {
304
+ td.event {
305
+ background-color: inherit;
306
+ }
307
+ }
308
+ td.event {
309
+ background-color: #ffffff;
310
+
311
+ .event-user img {
312
+ position: relative;
313
+ }
314
+ }
315
+ }
316
+ }
317
+ .mptt-navigation-tabs {
318
+ margin: 0 0 1rem;
319
+ list-style-type: none;
320
+ li {
321
+
322
+ display: inline-block;
323
+ padding: 0.25rem 0.5rem 0.25rem 0;
324
+ }
325
+ }
326
+ .mptt-shortcode-list {
327
+ display: none;
328
+
329
+ .mptt-column {
330
+ margin: 2rem 0;
331
+ .mptt-column-title {
332
+ margin: 2rem 0;
333
+ }
334
+
335
+ .mptt-events-list {
336
+ list-style: none;
337
+ margin: 2rem;
338
+ padding: 0;
339
+ .mptt-list-event {
340
+ list-style: none;
341
+ margin-bottom: 2rem;
342
+ padding: 0 0 0 2rem;
343
+ border-left: 3px solid @default_color;
344
+
345
+ .mptt-event-title {
346
+ margin: 0.25rem 0 0;
347
+ padding: 0;
348
+ }
349
+ .timeslot,
350
+ .event-user {
351
+ margin: 0.25rem 0 0;
352
+ padding: 0;
353
+ }
354
+ .event-description {
355
+ margin: 0.25rem 0 0;
356
+ padding: 0;
357
+ hyphens: auto;
358
+ }
359
+ .event-user {
360
+ margin: 0.25rem 0 0;
361
+ padding: 0;
362
+ }
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ &.mptt-table-fixed {
369
+ overflow-x: scroll;
370
+ .mptt-shortcode-table {
371
+ table-layout: auto;
372
+ }
373
+ thead, tbody {
374
+ width: 100%;
375
+ }
376
+ }
377
+
378
+ td{
379
+ vertical-align: middle;
380
+ }
381
+ }
382
+
383
+ /* ====== Shortcode end ====== */
384
+
385
+ /* ====== Media start ===== */
386
+ @media (max-width: 767px) {
387
+
388
+ .mptt-main-wrapper {
389
+ .mptt-content {
390
+ padding: 0 2rem 2rem;
391
+ width: 100%;
392
+ }
393
+ .mptt-sidebar {
394
+ padding: 0 2rem 2rem;
395
+ width: 100%;
396
+ }
397
+ }
398
+
399
+ .mptt-shortcode-wrapper {
400
+ .mptt-shortcode-list {
401
+ display: block;
402
+ }
403
+
404
+ &.mptt-table-fixed {
405
+ .mptt-shortcode-list {
406
+ display: none !important;;
407
+ }
408
+ .mptt-shortcode-table {
409
+ display: table;
410
+ td.event {
411
+ min-width: 200px;
412
+ }
413
+ }
414
+ }
415
+ &.mptt-table-responsive {
416
+ .mptt-shortcode-table {
417
+ display: none !important;
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ .mptt-clearfix {
424
+ *zoom: 1;
425
+ &:before,
426
+ &:after {
427
+ display: table;
428
+ content: "";
429
+ line-height: 0;
430
+ }
431
+ &:after {
432
+ clear: both;
433
+ }
434
+ }
435
+
436
+ .twentysixteen {
437
+ &.mptt-shortcode-wrapper {
438
+ .event-title {
439
+ box-shadow: none;
440
+ }
441
+ }
442
+ }
443
+
444
+ .mptt-theme-mode-event.event {
445
+ .event-user img {
446
+ position: relative;
447
+ }
448
+ }
449
+
450
+ .twentyfifteen {
451
+ &.mptt-shortcode-wrapper {
452
+ .event-title {
453
+ border-bottom: none;
454
+ }
455
+ }
456
+ }
457
+
458
+ .twentyfourteen {
459
+ .mptt-content {
460
+ width: 100%;
461
+ margin: 0;
462
+ }
463
+ }
media/less/theme.less ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Twenty Fourteen specific styles
3
+ */
4
+ .twentyfourteen {
5
+ .tfmp {
6
+ padding: 12px 10px 0;
7
+ max-width: 474px;
8
+ margin: 0 auto;
9
+
10
+ div.product.hentry.has-post-thumbnail {
11
+ margin-top: 0;
12
+ }
13
+
14
+ }
15
+ }
16
+
17
+ .twentyeleven {
18
+ }
19
+
20
+ .twentytwelve {
21
+
22
+ }
23
+
24
+ .twentythirteen {
25
+ h1 {
26
+ margin: 0;
27
+ }
28
+ ul {
29
+ padding: 0;
30
+ }
31
+ }
32
+
33
+ @media screen and (min-width: 673px) {
34
+ .twentyfourteen {
35
+ .tfmp {
36
+ padding-right: 30px;
37
+ padding-left: 30px;
38
+ }
39
+ }
40
+ }
41
+
42
+ @media screen and (min-width: 1040px) {
43
+ .twentyfourteen {
44
+ .tfmp {
45
+ padding-right: 15px;
46
+ padding-left: 15px;
47
+ }
48
+ }
49
+ }
50
+
51
+ @media screen and (min-width: 1110px) {
52
+ .twentyfourteen {
53
+ .tfmp {
54
+ padding-right: 30px;
55
+ padding-left: 30px;
56
+ }
57
+ }
58
+ }
59
+
60
+ @media screen and (min-width: 1218px) {
61
+ .twentyfourteen {
62
+ .tfmp {
63
+ margin-right: 54px;
64
+ }
65
+ }
66
+
67
+ .full-width {
68
+ .twentyfourteen {
69
+ .tfmp {
70
+ margin-right: auto;
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Twenty Fifteen specific styles
78
+ */
79
+ .twentyfifteen {
80
+ .t15mp {
81
+ padding-left: 7.6923%;
82
+ padding-right: 7.6923%;
83
+ padding-top: 7.6923%;
84
+ margin-bottom: 7.6923%;
85
+ background: #fff;
86
+ box-shadow: 0 0 1px rgba(0, 0, 0, 0.15);
87
+ }
88
+ .mptt-sidebar .widget.mptt-container {
89
+ padding: 0;
90
+ }
91
+ }
92
+
93
+ @media screen and (min-width: 38.75em) {
94
+ .twentyfifteen {
95
+ .t15mp {
96
+ margin-right: 7.6923%;
97
+ margin-left: 7.6923%;
98
+ margin-top: 8.3333%;
99
+ }
100
+ }
101
+ }
102
+
103
+ @media screen and (min-width: 59.6875em) {
104
+ .twentyfifteen {
105
+ .t15mp {
106
+ margin-left: 8.3333%;
107
+ margin-right: 8.3333%;
108
+ padding: 10%;
109
+ }
110
+ }
111
+
112
+ .single-product {
113
+ .twentyfifteen {
114
+ .entry-summary {
115
+ padding: 0 !important;
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Twenty Sixteen specific styles
123
+ */
124
+ .twentysixteen {
125
+ .site-main {
126
+ margin-right: 7.6923%;
127
+ margin-left: 7.6923%;
128
+ }
129
+
130
+ .entry-summary {
131
+ margin-right: 0;
132
+ margin-left: 0;
133
+ }
134
+ }
135
+
136
+ @media screen and (min-width: 44.375em) {
137
+ .twentysixteen {
138
+ .site-main {
139
+ margin-right: 23.0769%;
140
+ }
141
+ }
142
+ }
143
+
144
+ @media screen and (min-width: 56.875em) {
145
+ .twentysixteen {
146
+ .site-main {
147
+ margin-right: 0;
148
+ margin-left: 0;
149
+ }
150
+ }
151
+ }
152
+
153
+ /* theme styles */
154
+ .mptt-column {
155
+ .event[id^="event_columns_"] {
156
+ overflow: auto;
157
+ }
158
+ }
159
+
160
+ .mptt-theme-mode-event.event {
161
+ overflow: auto;
162
+ }
mp-timetable.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Plugin Name: Timetable and Event Schedule
5
+ * Plugin URI: https://motopress.com/products/timetable-event-schedule/
6
+ * Description: Smart time-management tool with a clean minimalist design for featuring your timetables and upcoming events.
7
+ * Version: 2.3.4
8
+ * Author: MotoPress
9
+ * Author URI: https://motopress.com
10
+ * License: GPLv2 or later
11
+ * Text Domain: mp-timetable
12
+ * Domain Path: /languages
13
+ */
14
+
15
+ /*
16
+ * This plugin contains hooks that allow you to edit, add and move content without the need to edit template files.
17
+ * This method protects against upgrade issues. There are several actions and filters you can use to modify content output.
18
+ * You can check \mp-timetable\classes\class-hooks.php for the list of hooks.
19
+ *
20
+ * Alternatively in "Developer Mode", you can copy template files from '/mp-timetable/templates/' folder to '/your-theme/mp-timetable/' to override them.
21
+ *
22
+ * The Timetable plugin also supports default WordPress templates hierarchy:
23
+ * https://developer.wordpress.org/themes/basics/template-hierarchy/#visual-overview
24
+ */
25
+
26
+ use mp_timetable\plugin_core\classes\Core;
27
+
28
+ define( "MP_TT_PLUGIN_NAME", 'mp-timetable' );
29
+ define( 'MP_TT_DEBUG', false );
30
+
31
+ register_activation_hook( __FILE__, array( Mp_Time_Table::init(), 'on_activation' ) );
32
+ register_deactivation_hook( __FILE__, array( 'Mp_Time_Table', 'on_deactivation' ) );
33
+ register_uninstall_hook( __FILE__, array( 'Mp_Time_Table', 'on_uninstall' ) );
34
+
35
+ add_action( 'plugins_loaded', array( 'Mp_Time_Table', 'init' ) );
36
+ add_action( 'wpmu_new_blog', array( 'Mp_Time_Table', 'on_create_blog' ), 10, 6 );
37
+ add_filter( 'wpmu_drop_tables', array( 'Mp_Time_Table', 'on_delete_blog' ) );
38
+
39
+ /**
40
+ * Class Mp_Time_Table
41
+ */
42
+ class Mp_Time_Table {
43
+
44
+ protected static $instance;
45
+
46
+ /**
47
+ * Mp_Time_Table constructor.
48
+ */
49
+ public function __construct() {
50
+ $this->include_all();
51
+ Core::get_instance()->init_plugin( 'mp_timetable' );
52
+ }
53
+
54
+ /**
55
+ * Include all files
56
+ */
57
+ public function include_all() {
58
+ /**
59
+ * Include Gump
60
+ */
61
+ require_once self::get_plugin_path() . 'classes/libs/class-gump.php';
62
+ /**
63
+ * Install Fire bug
64
+ */
65
+ require_once self::get_plugin_path() . 'classes/libs/FirePHPCore/fb.php';
66
+ /**
67
+ * Install Parsers
68
+ */
69
+ require_once self::get_plugin_path() . 'classes/libs/parsers.php';
70
+ /**
71
+ * Include Permalinks
72
+ */
73
+ require_once self::get_plugin_path() . 'classes/class-permalinks.php';
74
+ /**
75
+ * Include Core
76
+ */
77
+ require_once self::get_plugin_path() . 'classes/class-core.php';
78
+ /**
79
+ * Include module
80
+ */
81
+ require_once self::get_plugin_path() . 'classes/class-module.php';
82
+ /**
83
+ * Include Model
84
+ */
85
+ require_once self::get_plugin_path() . 'classes/class-model.php';
86
+
87
+ /**
88
+ * Include Controller
89
+ */
90
+ require_once self::get_plugin_path() . 'classes/class-controller.php';
91
+ /**
92
+ * Include State factory
93
+ */
94
+ require_once self::get_plugin_path() . 'classes/class-state-factory.php';
95
+
96
+ /**
97
+ * Include Preprocessor
98
+ */
99
+ require_once self::get_plugin_path() . 'classes/class-preprocessor.php';
100
+
101
+ /**
102
+ * include shortcodes
103
+ */
104
+ require_once( self::get_plugin_path() . 'classes/class-shortcode.php' );
105
+ /**
106
+ * include Widgets
107
+ */
108
+ require_once self::get_plugin_path() . 'classes/widgets/class-mp-timetable-widget.php';
109
+ /**
110
+ * Include view
111
+ */
112
+ require_once self::get_plugin_path() . 'classes/class-view.php';
113
+ /**
114
+ * Include hooks
115
+ */
116
+ require_once self::get_plugin_path() . 'classes/class-hooks.php';
117
+
118
+ /**
119
+ * Include blocks
120
+ */
121
+ require_once self::get_plugin_path() . 'classes/blocks/class-timetable-block.php';
122
+ }
123
+
124
+ /**
125
+ * Get plugin path
126
+ */
127
+ public static function get_plugin_path() {
128
+ return plugin_dir_path( __FILE__ );
129
+ }
130
+
131
+ /**
132
+ * @return Mp_Time_Table
133
+ */
134
+ public static function init() {
135
+ if ( null === self::$instance ) {
136
+ self::$instance = new self();
137
+ }
138
+
139
+ return self::$instance;
140
+ }
141
+
142
+ /**
143
+ * Retrieve relative to theme root path to templates.
144
+ *
145
+ * @return string
146
+ */
147
+ public static function get_template_path() {
148
+ return apply_filters( 'mptt_template_path', 'mp-timetable/' );
149
+ }
150
+
151
+ /**
152
+ * Retrieve relative to plugin root path to templates.
153
+ *
154
+ * @return string
155
+ */
156
+ public static function get_templates_path() {
157
+ return self::get_plugin_path() . 'templates/';
158
+ }
159
+
160
+ /**
161
+ * Get plugin part path
162
+ *
163
+ * @param string $part
164
+ *
165
+ * @return string
166
+ */
167
+ public static function get_plugin_part_path( $part = '' ) {
168
+ return self::get_plugin_path() . $part;
169
+ }
170
+
171
+ /**
172
+ * On activation
173
+ */
174
+ public static function on_activation() {
175
+ // Register post type
176
+ Core::get_instance()->register_all_post_type();
177
+
178
+ // Register taxonomy all
179
+ Core::get_instance()->register_all_taxonomies();
180
+
181
+ flush_rewrite_rules();
182
+
183
+ //Create table in not exists
184
+ Core::get_instance()->create_table();
185
+ }
186
+
187
+ /**
188
+ * On deactivation
189
+ */
190
+ public static function on_deactivation() {
191
+ flush_rewrite_rules();
192
+ }
193
+
194
+ /**
195
+ * On uninstall
196
+ */
197
+ public static function on_uninstall() {
198
+ do_action( 'mptt_on_uninstall' );
199
+ }
200
+
201
+ /**
202
+ * On create blog
203
+ *
204
+ * @param $blog_id
205
+ * @param $user_id
206
+ * @param $domain
207
+ * @param $path
208
+ * @param $site_id
209
+ * @param $meta
210
+ */
211
+ public static function on_create_blog( $blog_id, $user_id, $domain, $path, $site_id, $meta ) {
212
+ if ( is_plugin_active_for_network( self::get_plugin_name() . '/' . self::get_plugin_name() . '.php' ) ) {
213
+ switch_to_blog( $blog_id );
214
+ //Create table in not exists
215
+ Core::get_instance()->create_table();
216
+ restore_current_blog();
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Get plugin name
222
+ *
223
+ * @return string
224
+ */
225
+ public static function get_plugin_name() {
226
+ return dirname( plugin_basename( __FILE__ ) );
227
+ }
228
+
229
+ /**
230
+ * On blog creation
231
+ */
232
+ public static function on_delete_blog( $tables ) {
233
+
234
+ $tables[] = self::get_datatable();
235
+
236
+ return $tables;
237
+ }
238
+
239
+ /**
240
+ * Get data table name
241
+ *
242
+ * @return string
243
+ */
244
+ public static function get_datatable() {
245
+ global $wpdb;
246
+
247
+ return $wpdb->prefix . "mp_timetable_data";
248
+ }
249
+
250
+ /**
251
+ * Get plugin url
252
+ *
253
+ * @param bool|false $path
254
+ * @param string $pluginName
255
+ * @param string $sync
256
+ *
257
+ * @return string
258
+ */
259
+ static function get_plugin_url( $path = false, $pluginName = 'mp-timetable', $sync = '' ) {
260
+ return plugins_url() . '/' . $pluginName . '/' . $path . $sync;
261
+ }
262
+ }
readme.txt ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Timetable and Event Schedule by MotoPress ===
2
+ Contributors: MotoPress
3
+ Donate link: https://motopress.com/
4
+ Tags: schedule, timetable, calendar, event, events calendar, dates, event organizer, booking, appointments, upcoming events
5
+ Requires at least: 4.6
6
+ Tested up to: 5.3
7
+ Stable tag: trunk
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Smart event organizer and time-management tool with a clean minimalist design for featuring your timetables and upcoming events.
12
+
13
+ == Description ==
14
+
15
+ MotoPress Timetable and Event Schedule is an all-around organizer plugin developed to help you create and manage online schedules for a single or multiple events, customize the appearance of each event, add date, time, description and display all the needed items in a carefully-crafted timetable. It also comes with Upcoming events widget that will help you keep the sidebar clutter-free.
16
+
17
+ The plugin can be used for timetabling different types of events like various lessons, gym classes, festivals, conferences, ceremonies, case-studies, formal parties, concerts, and much more. It's handy in terms of backend management and maximum easy for your audience to use.
18
+
19
+ * Check [Timetable and Event Schedule Plugin Demo](https://mpttdemo.getmotopress.com/)
20
+ * Please find the step-by-step [instructions of working with the plugin](https://motopress.com/files/motopress-timetable-plugin-documentation.pdf)
21
+
22
+ = Key advantages =
23
+
24
+ **Responsive design.** It's optimized to be viewed perfectly on different devices. A good step forward, the plugin was supplied with the ability to manually adjust the way to show your timetable on mobile devices and desktops.
25
+
26
+ **A well thought-out toolkit of shortcode settings.** It eliminates the difficulties of timetabling as all preferable settings can be applied in minutes. Each setting is supplied with sufficient clarifications to ensure you coordinate and edit your events fast without additional help.
27
+
28
+ **Handy event filtering.** The visitors can filter the timetable to display the only events they are interested in.
29
+
30
+ **Color controls.** Highlight important activities by presenting them in different colors. Additionally, it simplifies and speeds up the search as the needed events marked with the same color can be noticed faster even without being filtered. Various color markers can become helpful in making the timetable more colorful or in customizing it to fit your website color scheme.
31
+
32
+ **More precise visual time frames.** Hourly time frames are generally large enough for showing the events, but using the MotoPress Timetable plugin you still can increase them by setting the timetable to show up to 15 minutes accurate time in the left 'time' column.
33
+
34
+ **Flexibility.** If any unexpected delays or total dates' changes take place, your timetable can bend easily in one direction or another thanks to a couple of qucik time edits in the backend.
35
+
36
+ = Main features =
37
+ * Several column types
38
+ * Selecting/deselecting the preferable columns and events to be displayed in the timetable
39
+ * Ability to add event tags and categories
40
+ * Ability to display the events by the appropriate categories
41
+ * Hour measure to be displayed in the left timetable column to show more accurate duration of each activity (event)
42
+ * Several filter styles: drop-down list and tabs
43
+ * Option to display/hide 'All Events' view mode, hours column and empty rows
44
+ * Customizable event parameters (title, time, subtitle, etc.) and the ability to display only preferable ones in the timetable
45
+ * Featured images for individual events
46
+ * Opportunity to set event URL to link it any external website
47
+ * Text align options for event blocks
48
+ * Unique IDs for multiple timetables on a single page
49
+ * Color settings for background, background hover, text, and text hover
50
+ * Export/import of your data
51
+
52
+ Proud developers of Timetable plugin and the biggest set of blocks for Gutenberg - [Getwid WordPress Blocks](https://wordpress.org/plugins/getwid/).
53
+
54
+ == Installation ==
55
+
56
+ 1. Upload the plugin files to the /wp-content/plugins/ directory
57
+ 2. Activate the plugin through the 'Plugins' menu in WordPress. You'll find 'Timetable' on your main WordPress dashboard.
58
+
59
+ == Copyright ==
60
+
61
+ Timetable and Event Schedule plugin, Copyright (C) 2019, MotoPress https://motopress.com/
62
+ Timetable and Event Schedule plugin is distributed under the terms of the GNU GPL.
63
+
64
+ == Screenshots ==
65
+ 1. Timetable
66
+ 2. Column
67
+ 3. Timeslots
68
+
69
+ == Credits ==
70
+
71
+ Plugin bundles the following third-party resources:
72
+
73
+ * GUMP, Copyright (c) 2015 wixelhq.com, MIT License
74
+ * jQuery UI, Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT
75
+ * Spectrum Colorpicker, by Brian Grinstead, MIT License
76
+ * jBox, by Stephan Wagner, MIT License
77
+ * jQuery UI Timepicker, Copyright 2010-2013, Francois Gelinas, Dual licensed under the MIT or GPL Version 2 licenses.
78
+
79
+
80
+ == Changelog ==
81
+
82
+ = 2.3.4, Sep 25 2019 =
83
+ * Bug fix: fixed an issue with UTC timezone offset.
84
+
85
+ = 2.3.3, Sep 20 2019 =
86
+ * Bug fix: fixed the issue with events disappearing on hover in Chrome.
87
+
88
+ = 2.3.2 =
89
+ * Bug fix: fixed an issue in IE browser.
90
+
91
+ = 2.3.1 =
92
+ * Bug fix: fixed the issue with opening PHP tag.
93
+
94
+ = 2.3.0 =
95
+ * Added support for WordPress 5.0.
96
+
97
+ = 2.2.1 =
98
+ * Bug fix: fixed an issue with URL hash (not-set:all).
99
+
100
+ = 2.2.0 =
101
+ * Added filters for register_post_type and register_taxonomy functions.
102
+ * Added permalink configuration option in Settings > Permalinks.
103
+
104
+ = 2.1.12 =
105
+ * Bug fix: fixed an issue in IE browser.
106
+
107
+ = 2.1.11 =
108
+ * Added translations into the Persian language.
109
+
110
+ = 2.1.10 =
111
+ * Improved compatibility with WooCommerce Memberships plugin.
112
+ * Better W3C validation.
113
+
114
+ = 2.1.9 =
115
+ * Bug fix: fixed the issue when event was not visible in the timetable if it starts at 00:00.
116
+
117
+ = 2.1.8 =
118
+ * Improved compatibility with javascript-disabled browsers.
119
+
120
+ = 2.1.7 =
121
+ * Bug fix: fixed the issue when events were not filtered by the selected category in a widget.
122
+ * Bug fix: fixed the issue when event was not visible in the timetable if it ends the next day.
123
+ * Minor bugfixes and improvements.
124
+
125
+ = 2.1.6 =
126
+ * New dashboard icons.
127
+
128
+ = 2.1.5 =
129
+ * Bug fix: fixed the issue when table is not visible if events filter is set to 'none'.
130
+
131
+ = 2.1.4 =
132
+ * Bug fix: fixed internal issue.
133
+
134
+ = 2.1.3 =
135
+ * Added the ability to merge cells with common events.
136
+ * Added the ability to set vertical alignment in the table.
137
+ * Added the ability to set custom CSS class for table shortcode.
138
+ * Added the ability to hide filter control on the top of the table.
139
+ * Bug fix: fixed the issue when table ID didn't output on the site.
140
+
141
+ = 2.1.2 =
142
+ * Bug fix: fixed the issue when plugin overrides default archive template.
143
+ * Bug fix: fixed the issue in upcoming events widget when events were not sorted by days.
144
+
145
+ = 2.1.1 =
146
+ * Bug fix: fixed the issue with shortcode tempalte.
147
+
148
+ = 2.1.0 =
149
+ * Added the ability to override templates in a theme.
150
+ * Bug fix: fixed the issue on WordPress multisite when Event wasn't saving.
151
+ * Bug fix: fixed the issue when timeslots didn't show if Event is in the Draft status.
152
+
153
+ = 2.0.4 =
154
+ * Bug fix: fixed an issue with post template on search results page.
155
+
156
+ = 2.0.3 =
157
+ * Bug fix: fixed a link to create new column from event screen.
158
+ * Improved usernames in Event Head dropdown.
159
+
160
+ = 2.0.2 =
161
+ * Minor bugfixes and improvements.
162
+
163
+ = 2.0.1 =
164
+ * Bug fix: fixed an issue with template override.
165
+
166
+ = 2.0.0 =
167
+ * We improved compatibility with your theme styles in this update. If you still stick to a previous version, simply change the template mode option in the plugin settings.
168
+ * Added the ability to set font size for timetable shortcode so you can make your text bigger or smaller.
169
+
170
+ = 1.1.6 =
171
+ * Bug fix: fixed an issue with empty rows.
172
+
173
+ = 1.1.5 =
174
+ * Bug fix: fixed an issue with post view in search results.
175
+
176
+ = 1.1.4 =
177
+ * Improved compatibility with Polylang plugin.
178
+
179
+ = 1.1.3 =
180
+ * Fixed an issue with categories filter in widget.
181
+
182
+ = 1.1.2 =
183
+ * Fixed an issue with posts order.
184
+
185
+ = 1.1.0 =
186
+ * Improved events sorting.
187
+ * Minor bugfixes and improvements.
188
+
189
+ = 1.0.7 =
190
+ * Improved events and columns sorting by date.
191
+ * Comments section added to event.
192
+ * Minor bugfixes and improvements.
193
+
194
+ = 1.0.6 =
195
+ * Fixed an issue in column view.
196
+
197
+ = 1.0.5 =
198
+ * Improved compatibility with IE10+
199
+
200
+ = 1.0.4 =
201
+ * Minor bugfixes and improvements.
202
+
203
+ = 1.0.3 =
204
+ * Minor bugfixes and improvements.
templates-functions/action-mp-column-functions.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use mp_timetable\classes\controllers\Controller_Column;
3
+
4
+ function mptt_column_template_content_title() { ?>
5
+ <h1 class="column-title"><?php the_title(); ?></h1>
6
+ <?php }
7
+
8
+ function mptt_column_template_content_post_content() { ?>
9
+ <div class="column-content">
10
+ <?php the_content(); ?>
11
+ </div>
12
+ <?php }
13
+
14
+ function mptt_column_template_content_events_list() {
15
+ $events = mptt_get_column_events();
16
+ do_action('mptt-before-column-events', $events);
17
+ ?>
18
+ <ul class="mptt-column <?php echo apply_filters('mptt_events_list_class', 'events-list') ?>">
19
+ <?php foreach ($events as $event): ?>
20
+ <li class="event" id="event_columns_<?php echo $event->event_id ?>">
21
+
22
+ <?php if (has_post_thumbnail($event->event_id)) {
23
+
24
+ echo wp_get_attachment_image(
25
+ get_post_thumbnail_id($event->event_id),
26
+ apply_filters('mptt_event_thumbnail_size', 'thumbnail'),
27
+ false,
28
+ array('class' => "alignleft event-thumbnail", 'alt' => get_the_title($event->event_id))
29
+ );
30
+
31
+ } else { ?>
32
+ <img class="event-thumbnail event-thumbnail-default"
33
+ src="<?php echo Mp_Time_Table::get_plugin_url() . 'media/css/images/column_icon.png' ?>"
34
+ alt="<?php echo get_the_title($event->event_id); ?>">
35
+ <?php } ?>
36
+
37
+ <h4 class="event-title">
38
+ <a href="<?php echo $event->post->timetable_disable_url == '1' ? '#' : ($event->post->timetable_custom_url != "" ? $event->post->timetable_custom_url : get_permalink($event->event_id)) ?>"
39
+ class="event-link">
40
+ <?php echo get_the_title($event->event_id); ?>
41
+ </a>
42
+ </h4>
43
+
44
+ <p class="timeslot">
45
+ <time datetime="<?php echo $event->event_start; ?>"
46
+ class="timeslot-start"><?php echo date(get_option('time_format'), strtotime($event->event_start)); ?></time><?php echo apply_filters('mptt_timeslot_delimiter', ' - '); ?>
47
+ <time datetime="<?php echo $event->event_end; ?>" class="timeslot-end"><?php echo date(get_option('time_format'), strtotime($event->event_end)); ?></time>
48
+ </p>
49
+
50
+ <?php if (!empty($event->post->sub_title)) { ?>
51
+ <p class="event-subtitle"><?php echo $event->post->sub_title ?></p>
52
+ <?php } ?>
53
+
54
+ <?php if (!empty($event->description)) { ?>
55
+ <p class="event-description"><?php echo $event->description; ?></p>
56
+ <?php } ?>
57
+
58
+ <?php if (!empty($event->user)) { ?>
59
+ <p class="event-user"><a href="<?php echo get_author_posts_url($event->user->ID); ?>"
60
+ title="<?php the_title_attribute(array('post' => $event->event_id)); ?>"><?php echo get_avatar($event->user->ID, apply_filters('mptt-column-user-avatar-size', 32), '', $event->user->display_name); ?>
61
+ <?php echo $event->user->display_name ?></a></p>
62
+ <?php } ?>
63
+ <div class="mptt-clearfix"></div>
64
+ </li>
65
+ <?php endforeach; ?>
66
+ </ul>
67
+ <?php
68
+ do_action('mptt-after-column-events', $events);
69
+ }
70
+
71
+ /**
72
+ * Get column events
73
+ *
74
+ * @return array
75
+ */
76
+ function mptt_get_column_events() {
77
+ global $post;
78
+ $data = Controller_Column::get_instance()->action_page_view($post);
79
+ return !empty($data) ? $data : array();
80
+ }
templates-functions/action-shortcode-functions.php ADDED
@@ -0,0 +1,694 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ use mp_timetable\classes\models\Settings;
3
+ use mp_timetable\plugin_core\classes\View;
4
+
5
+ /**
6
+ * Before content
7
+ */
8
+ function mptt_shortcode_template_before_content() {
9
+ global $mptt_shortcode_data;
10
+ $wrapper_class = mptt_popular_theme_class();
11
+ $id = empty( $mptt_shortcode_data[ 'params' ][ 'id' ] ) ? '' : $mptt_shortcode_data[ 'params' ][ 'id' ];
12
+ ?>
13
+ <div <?php if ( !empty($id) ) echo 'id="' . $id . '" '; ?>class="<?php echo apply_filters( 'mptt_shortcode_wrapper_class', 'mptt-shortcode-wrapper' . $wrapper_class . ( $mptt_shortcode_data[ 'params' ][ 'responsive' ] == '0' ? ' mptt-table-fixed' : ' mptt-table-responsive' ) ) ?>">
14
+ <?php
15
+ }
16
+
17
+ /**
18
+ * After content
19
+ */
20
+ function mptt_shortcode_template_after_content() { ?>
21
+ </div>
22
+ <?php }
23
+
24
+ /**
25
+ * Filter contents
26
+ */
27
+ function mptt_shortcode_template_content_filter() {
28
+ global $mptt_shortcode_data;
29
+ $unique_events = empty( $mptt_shortcode_data[ 'unique_events' ] ) ? array() : $mptt_shortcode_data[ 'unique_events' ];
30
+ $style = '';
31
+ if ( count( $mptt_shortcode_data[ 'events_data' ][ 'unique_events' ] ) < 2 ) {
32
+ $style = ' style="display:none;"';
33
+ }
34
+ $display_label = $mptt_shortcode_data[ 'params' ][ 'hide_label' ] ? 'display: none' : '';
35
+
36
+ if ( $mptt_shortcode_data[ 'params' ][ 'view' ] == 'dropdown_list' ) { ?>
37
+ <select class="<?php echo apply_filters( 'mptt_shortcode_navigation_select_class', 'mptt-menu mptt-navigation-select' ) ?>"<?php echo $style ?>>
38
+ <?php if ( ! $mptt_shortcode_data[ 'params' ][ 'hide_label' ] ) { ?>
39
+ <option value="all"><?php echo ( strlen( trim( $mptt_shortcode_data[ 'params' ][ 'label' ] ) ) ) ? trim( $mptt_shortcode_data[ 'params' ][ 'label' ] ) : __( 'All Events', 'mp-timetable' ) ?></option>
40
+ <?php } else { ?>
41
+ <option value="all"></option>
42
+ <?php }
43
+ if ( ! empty( $unique_events ) ):
44
+ foreach ( $unique_events as $event ): ?>
45
+ <option value="<?php echo $event->post->post_name ?>"><?php echo $event->post->post_title ?></option>
46
+ <?php endforeach;
47
+ endif; ?>
48
+ </select>
49
+ <?php } elseif ( $mptt_shortcode_data[ 'params' ][ 'view' ] == 'tabs' ) { ?>
50
+ <ul class="<?php echo apply_filters( 'mptt_shortcode_navigation_tabs_class', 'mptt-menu mptt-navigation-tabs' ) ?>" <?php echo $style ?>>
51
+ <li style="<?php echo $display_label ?>">
52
+ <a title="<?php echo ( strlen( trim( $mptt_shortcode_data[ 'params' ][ 'label' ] ) ) ) ? trim( $mptt_shortcode_data[ 'params' ][ 'label' ] ) : __( 'All Events', 'mp-timetable' ) ?>"
53
+ href="#all" onclick="event.preventDefault();"><?php echo ( strlen( trim( $mptt_shortcode_data[ 'params' ][ 'label' ] ) ) ) ? trim( $mptt_shortcode_data[ 'params' ][ 'label' ] ) : __( 'All Events', 'mp-timetable' ) ?>
54
+ </a>
55
+ </li>
56
+ <?php if ( ! empty( $unique_events ) ): ?>
57
+ <?php foreach ( $unique_events as $event ): ?>
58
+ <li><a title="<?php echo $event->post->post_title ?>" href="#<?php echo $event->post->post_name ?>" onclick="event.preventDefault();"><?php echo $event->post->post_title ?></a></li>
59
+ <?php endforeach;
60
+ endif; ?>
61
+ </ul>
62
+ <?php }
63
+ }
64
+
65
+ /**
66
+ * Content static table
67
+ */
68
+ function mptt_shortcode_template_content_static_table() {
69
+ global $mptt_shortcode_data;
70
+
71
+ mptt_shortcode_template_event( $mptt_shortcode_data );
72
+
73
+ if ( isset( $mptt_shortcode_data[ 'unique_events' ] ) && is_array( $mptt_shortcode_data[ 'unique_events' ] ) ) {
74
+ foreach ( $mptt_shortcode_data[ 'unique_events' ] as $ev ) {
75
+ mptt_shortcode_template_event( $mptt_shortcode_data, $ev->post );
76
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Event template
82
+ *
83
+ * @param $mptt_shortcode_data
84
+ * @param mixed $post
85
+ */
86
+ function mptt_shortcode_template_event( $mptt_shortcode_data, $post = 'all' ) {
87
+ $params = $mptt_shortcode_data[ 'params' ];
88
+
89
+ $column_events = mptt_get_columns_events( $mptt_shortcode_data, $post );
90
+
91
+ // Get start / end row index
92
+ $bounds = mptt_shortcode_get_table_cell_bounds( $column_events, $params );
93
+
94
+ $hide_empty_rows = $params[ 'hide_empty_rows' ];
95
+ $font_size = ! empty( $params[ 'font_size' ] ) ? ' font-size:' . $params[ 'font_size' ] . ';' : '';
96
+ $row_height = $params[ 'row_height' ];
97
+ $table_class = apply_filters( 'mptt_shortcode_static_table_class', 'mptt-shortcode-table' ) . ' ' . $params[ 'custom_class' ];
98
+ $table_class .= Settings::get_instance()->is_plugin_template_mode() ? '' : ' mptt-theme-mode';
99
+
100
+ $data_grouped_by_row = mptt_make_data_shortcode( $bounds, $mptt_shortcode_data, $column_events );
101
+
102
+ ?>
103
+ <table class="<?php echo ! empty( $table_class ) ? $table_class : ''; ?>" id="#<?php echo is_object( $post ) ? $post->post_name : $post; ?>" style="display:none; <?php echo $font_size; ?>" data-hide_empty_row="<?php echo $hide_empty_rows; ?>">
104
+ <?php echo View::get_instance()->get_template_html( 'shortcodes/table-header', array( 'header_items' => $data_grouped_by_row[ 'table_header' ], 'params' => $params ) ); ?>
105
+ <tbody>
106
+ <?php if ( isset( $data_grouped_by_row[ 'rows' ] ) && is_array( $data_grouped_by_row[ 'rows' ] ) ) {
107
+
108
+ foreach ( $data_grouped_by_row[ 'rows' ] as $row_key => $row ) {
109
+ if ( ! $row[ 'show' ] && $params[ 'hide_empty_rows' ] ) {
110
+ continue;
111
+ } ?>
112
+ <tr class="mptt-shortcode-row-<?php echo $row_key ?>" data-index="<?php echo $row_key ?>">
113
+ <?php $cells = $data_grouped_by_row[ 'rows' ][ $row_key ][ 'cells' ];
114
+
115
+ foreach ( $cells as $key_event => $cell ) {
116
+
117
+ if ( isset( $cell[ 'time_cell' ] ) && filter_var( $cell[ 'time_cell' ], FILTER_VALIDATE_BOOLEAN, array( 'options' => array( 'default' => false ) ) ) ) { ?>
118
+ <td class="mptt-shortcode-hours" style="<?php echo 'height:' . $row_height . 'px;'; ?>"><?php echo $cell[ 'title' ] ?></td>
119
+ <?php continue;
120
+ }
121
+
122
+ if ( ! $cell[ 'hide' ] ) { ?>
123
+ <td class="mptt-shortcode-event <?php echo mptt_is_grouped_event_class( $cell ) ?> mptt-event-vertical-<?php echo $params[ 'text_align_vertical' ] ?>" data-column-id="<?php echo $cell[ 'column_id' ] ?>" colspan="<?php echo ! isset( $cell[ 'count' ] ) ? '' : $cell[ 'count' ] ?>" data-row_height="<?php echo $row_height; ?>" style="<?php echo 'height:' . $row_height . 'px;'; ?>">
124
+ <?php
125
+ $height = 100 / count( $cell[ 'events' ] );
126
+ $top = 0;
127
+ foreach ( $cell[ 'events' ] as $event ) {
128
+
129
+ if ( ! empty( $event[ 'id' ] ) && filter_var( $event[ 'id' ], FILTER_VALIDATE_INT ) ) {
130
+ View::get_instance()->get_template( 'shortcodes/event-container', array( 'item' => $event, 'params' => $params, 'height' => $height, 'top' => $top ) );
131
+ $top += $height;
132
+ }
133
+
134
+ } ?>
135
+ </td>
136
+ <?php }
137
+ } ?>
138
+ </tr>
139
+ <?php }
140
+
141
+ } ?>
142
+ </tbody>
143
+ </table>
144
+ <?php
145
+ }
146
+
147
+ /**
148
+ * Check row has items
149
+ *
150
+ * @param $i
151
+ *
152
+ * @param $column_events
153
+ *
154
+ * @return bool
155
+ */
156
+ function mptt_shortcode_row_has_items( $i, $column_events ) {
157
+
158
+ foreach ( $column_events as $column_id => $events_list ) {
159
+
160
+ if ( ! empty( $column_events[ $column_id ] ) ) {
161
+ foreach ( $events_list as $key_events => $item ) {
162
+ if ( $item->start_index <= $i && $i < $item->end_index ) {
163
+ return true;
164
+ }
165
+ }
166
+ }
167
+
168
+ }
169
+
170
+ return false;
171
+ }
172
+
173
+ /**
174
+ * Get table cell bounds
175
+ *
176
+ * @param $column_events
177
+ * @param $params
178
+ *
179
+ * @return array
180
+ */
181
+ function mptt_shortcode_get_table_cell_bounds( $column_events, $params ) {
182
+ $hide_empty_rows = $params[ 'hide_empty_rows' ];
183
+
184
+ if ( $hide_empty_rows ) {
185
+ $min = - 1;
186
+ $max = - 1;
187
+ foreach ( $column_events as $events ) {
188
+ foreach ( $events as $item ) {
189
+ if ( isset($item->start_index) && isset($item->end_index) ) {
190
+ $min = ( $min === - 1 ) ? $item->start_index : $min;
191
+ $max = ( $max === - 1 ) ? $item->end_index : $max;
192
+ $min = ( $item->start_index < $min ) ? $item->start_index : $min;
193
+ $max = ( $item->end_index > $max ) ? $item->end_index : $max;
194
+ }
195
+ }
196
+ }
197
+ } else {
198
+ $min = 0;
199
+ $max = 23 / $params[ 'increment' ];
200
+ }
201
+
202
+ return array( 'start' => $min, 'end' => $max );
203
+ }
204
+
205
+ /**
206
+ * Content responsive table
207
+ */
208
+ function mptt_shortcode_template_content_responsive_table() {
209
+ global $mptt_shortcode_data;
210
+ if ( $mptt_shortcode_data[ 'params' ][ 'responsive' ] ) { ?>
211
+ <div class="<?php echo apply_filters( 'mptt_shortcode_list_view_class', 'mptt-shortcode-list' ) . ' ' . $mptt_shortcode_data[ 'params' ][ 'custom_class' ] ?>">
212
+ <?php if ( ! empty( $mptt_shortcode_data[ 'events_data' ] ) ):
213
+ foreach ( $mptt_shortcode_data[ 'events_data' ][ 'column' ] as $column ): ?>
214
+ <div class="mptt-column">
215
+ <h3 class="mptt-column-title"><?php echo $column->post_title ?></h3>
216
+ <ul class="mptt-events-list">
217
+ <?php if ( ! empty( $mptt_shortcode_data[ 'events_data' ][ 'column_events' ][ $column->ID ] ) ):
218
+ foreach ( $mptt_shortcode_data[ 'events_data' ][ 'column_events' ][ $column->ID ] as $event ) : ?>
219
+ <li class="mptt-list-event" data-event-id="<?php echo $event->post->post_name ?>"
220
+ <?php if ( ! empty( $event->post->color ) ) {
221
+ echo 'style="border-left-color:' . $event->post->color . ';"';
222
+ } ?>>
223
+ <?php if ( $mptt_shortcode_data[ 'params' ][ 'title' ] ):
224
+ $disable_url = (bool) $event->post->timetable_disable_url || (bool) $mptt_shortcode_data[ 'params' ][ 'disable_event_url' ];
225
+ if ( ! $disable_url ) { ?>
226
+ <a title="<?php echo $event->post->post_title; ?>"
227
+ href="<?php echo ( $event->post->timetable_custom_url != "" ) ? $event->post->timetable_custom_url : get_permalink( $event->event_id ); ?>"
228
+ class="mptt-event-title">
229
+ <?php }
230
+ echo $event->post->post_title;
231
+
232
+ if ( ! $disable_url ) { ?>
233
+ </a>
234
+ <?php }
235
+
236
+ endif;
237
+ if ( $mptt_shortcode_data[ 'params' ][ 'time' ] ): ?>
238
+ <p class="timeslot">
239
+ <time datetime="<?php echo $event->event_start; ?>" class="timeslot-start"><?php echo date( get_option( 'time_format' ), strtotime( $event->event_start ) ); ?></time>
240
+ <span class="timeslot-delimiter"><?php echo apply_filters( 'mptt_timeslot_delimiter', ' - ' ); ?></span>
241
+ <time datetime="<?php echo $event->event_end; ?>" class="timeslot-end"><?php echo date( get_option( 'time_format' ), strtotime( $event->event_end ) ); ?></time>
242
+ </p>
243
+ <?php endif;
244
+ if ( $mptt_shortcode_data[ 'params' ][ 'description' ] ): ?>
245
+ <p class="event-description">
246
+ <?php echo $event->description ?>
247
+ </p>
248
+ <?php endif;
249
+ if ( $mptt_shortcode_data[ 'params' ][ 'user' ] && ( $event->user_id != '-1' ) ): ?>
250
+ <p class="event-user"><?php $user_info = get_userdata( $event->user_id );
251
+ if ( $user_info ) {
252
+ echo get_avatar( $event->user_id, apply_filters( 'mptt-event-user-avatar-size', 24 ), '', $user_info->data->display_name ) . ' ';
253
+ echo $user_info->data->display_name;
254
+ } ?></p>
255
+ <?php endif; ?>
256
+ </li>
257
+ <?php endforeach;
258
+ endif; ?>
259
+ </ul>
260
+ </div>
261
+ <?php endforeach;
262
+ endif; ?>
263
+ </div>
264
+ <?php }
265
+ }
266
+
267
+ /**
268
+ * Sidebar
269
+ */
270
+ function mptt_sidebar() {
271
+ global $post;
272
+ View::get_instance()->get_template( 'templates-actions/action-sidebar', array( 'post' => $post ) );
273
+ }
274
+
275
+ /**
276
+ * Make data shortcode
277
+ *
278
+ * @param $bounds
279
+ * @param $mptt_shortcode_data
280
+ * @param $column_events
281
+ *
282
+ * @return array
283
+ */
284
+ function mptt_make_data_shortcode( $bounds, $mptt_shortcode_data, $column_events ) {
285
+ $data = array( 'rows' => array() );
286
+ $amount_rows = 23 / $mptt_shortcode_data[ 'params' ][ 'increment' ];
287
+
288
+ $data[ 'table_header' ] = mptt_get_header_row( $mptt_shortcode_data );
289
+
290
+ foreach ( $column_events as $column_id => $events_list ) {
291
+
292
+ foreach ( $events_list as $event_key => $item ) {
293
+ if ( isset( $item->resolve ) ) {
294
+ unset( $item->resolve );
295
+ }
296
+ }
297
+ }
298
+
299
+ for ( $row_index = $bounds[ 'start' ]; $row_index <= $bounds[ 'end' ]; $row_index ++ ) {
300
+
301
+ $table_time_cell = get_time_cell( $row_index, $amount_rows, $mptt_shortcode_data[ 'params' ][ 'increment' ] );
302
+
303
+ list( $row_cells, $column_events ) = mptt_get_row_events( $column_events, $row_index );
304
+
305
+ if ( $mptt_shortcode_data[ 'params' ][ 'group' ] ) {
306
+ $row_cells = mptt_group_identical_row_events( $row_cells );
307
+ }
308
+
309
+ $data[ 'rows' ][ $row_index ][ 'cells' ] = $row_cells;
310
+ $data[ 'rows' ][ $row_index ][ 'show' ] = mptt_shortcode_row_has_items( $row_index, $column_events );
311
+
312
+ if ( ! $mptt_shortcode_data[ 'params' ][ 'hide_hrs' ] ) {
313
+ array_unshift( $data[ 'rows' ][ $row_index ][ 'cells' ], array( 'time_cell' => true, 'title' => date( get_option( 'time_format' ), strtotime( $table_time_cell ) ) ) );
314
+ }
315
+ }
316
+
317
+ return $data;
318
+ }
319
+
320
+ /**
321
+ * Get time cell
322
+ *
323
+ * @param $row_index
324
+ * @param $amount_rows
325
+ * @param $increment
326
+ *
327
+ * @return string
328
+ */
329
+ function get_time_cell( $row_index, $amount_rows, $increment ) {
330
+ $tm = $row_index * $increment;
331
+
332
+ if ( floor( $tm ) == $tm ) {
333
+ $time = $tm . ':00';
334
+
335
+ return $time;
336
+ } else {
337
+ if ( $amount_rows == 46 ) {
338
+ $time = floor( $tm ) . ':30';
339
+
340
+ return $time;
341
+ } else {
342
+ $tm_position = explode( '.', $tm );
343
+
344
+ if ( $tm_position[ 1 ] == 25 ) {
345
+ $mnts = ':15';
346
+ } elseif ( $tm_position[ 1 ] == 5 ) {
347
+ $mnts = ':30';
348
+ } else {
349
+ $mnts = ':45';
350
+ }
351
+
352
+ $time = floor( $tm ) . $mnts;
353
+
354
+ return $time;
355
+ }
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Add group attribute to event
361
+ *
362
+ * @param $bounds
363
+ * @param $events_array
364
+ *
365
+ * @return mixed
366
+ */
367
+ function group_events( $bounds, $events_array ) {
368
+ $output_array = array();
369
+
370
+ foreach ( $events_array as $column_id => $events_list ) {
371
+
372
+ if ( ! empty( $events_array[ $column_id ] ) ) {
373
+
374
+ foreach ( $events_list as $key_events => $item ) {
375
+
376
+ if ( $bounds[ 'end' ] <= $item->end_index && $bounds[ 'start' ] >= $item->start_index ) {
377
+ continue;
378
+ } else {
379
+ $key_count = $item->start_index . '_' . $item->event_id . '_' . $item->end_index;
380
+
381
+ if ( ! isset( $output_array[ $key_count ] ) ) {
382
+ $output_array[ $key_count ] = array( 'count' => 0, 'output' => false );
383
+ $output_array[ $key_count ][ 'count' ] ++;
384
+ } else {
385
+ $output_array[ $key_count ][ 'count' ] ++;
386
+ }
387
+
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ return $output_array;
394
+ }
395
+
396
+ /**
397
+ * Header row
398
+ *
399
+ * @param $mptt_shortcode_data
400
+ *
401
+ * @return array
402
+ */
403
+ function mptt_get_header_row( $mptt_shortcode_data ) {
404
+ $header_array = array( '0' => array( 'output' => false ) );
405
+ $show_hrs = ! $mptt_shortcode_data[ 'params' ][ 'hide_hrs' ];
406
+
407
+ if ( $show_hrs ):
408
+ $header_array[ 0 ] = array( 'output' => true, 'id' => '', 'title' => '' );
409
+ endif;
410
+
411
+ if ( ! empty( $mptt_shortcode_data[ 'events_data' ][ 'column' ] ) ) {
412
+ foreach ( $mptt_shortcode_data[ 'events_data' ][ 'column' ] as $column ):
413
+ $header_array[] = array( 'output' => true, 'id' => $column->ID, 'title' => $column->post_title );
414
+ endforeach;
415
+ }
416
+
417
+ return $header_array;
418
+ }
419
+
420
+ /**
421
+ * Row events
422
+ *
423
+ * @param $column_events
424
+ * @param $row_index
425
+ *
426
+ * @return array
427
+ */
428
+ function mptt_get_row_events( $column_events, $row_index ) {
429
+ $events = array();
430
+ $default_count = 1;
431
+ $i = 0;
432
+
433
+ foreach ( $column_events as $column_id => $events_list ) {
434
+ $empty = true;
435
+
436
+ foreach ( $events_list as $event_key => $item ) {
437
+ if ( isset( $item->resolve ) && $item->resolve ) {
438
+
439
+ continue;
440
+ }
441
+ $group = false;
442
+ if ( ! empty( $current ) ) {
443
+ if ( ( $item->end_index <= $current[ 'end_index' ] ) ) {
444
+ $group = true;
445
+ }
446
+ }
447
+
448
+ if ( $item->start_index == $row_index || $group ) {
449
+
450
+ //create temp event data for generate hash
451
+ $temp = (array) $item;
452
+
453
+ //clear data before make hash to compare
454
+ if ( $temp[ 'id' ] ) {
455
+ unset( $temp[ 'id' ] );
456
+ unset( $temp[ 'column_id' ] );
457
+ }
458
+
459
+ $event = array(
460
+ 'id' => $item->id,
461
+ 'hash' => md5( serialize( $temp ) ),
462
+ 'start_index' => $item->start_index,
463
+ 'end_index' => $item->end_index,
464
+ 'event_start' => $item->event_start,
465
+ 'event_end' => $item->event_end,
466
+ 'column_id' => $item->column_id,
467
+ 'event_id' => $item->event_id,
468
+ 'event' => true,
469
+ 'user_id' => $item->user_id,
470
+ 'description' => trim( $item->description ),
471
+ 'order' => $event_key,
472
+ 'hide' => $group ? true : false
473
+ );
474
+
475
+ if ( ! $group ) {
476
+ $events[ $i ][ 'events' ][ $event[ 'hash' ] ] = $event;
477
+ $events[ $i ][ 'count' ] = $default_count;
478
+ $events[ $i ][ 'grouped' ] = false;
479
+ $events[ $i ][ 'column_id' ] = $column_id;
480
+ $events[ $i ][ 'hide' ] = false;
481
+ } else {
482
+
483
+ $events[ $i ][ 'hide' ] = false;
484
+ $events[ $i ][ 'column_id' ] = $column_id;
485
+ $events[ $i ][ 'events' ][ $event[ 'hash' ] ] = $event;
486
+
487
+ }
488
+
489
+ $column_events[ $column_id ][ $event_key ]->resolve = true;
490
+
491
+ $empty = false;
492
+
493
+ if ( empty( $current ) ) {
494
+ $current = array( 'start_index' => $item->start_index, 'end_index' => $item->end_index );
495
+ } else {
496
+ $current[ 'start_index' ] = $current[ 'start_index' ] >= $item->start_index ? $item->start_index : $current[ 'start_index' ];
497
+ $current[ 'end_index' ] = $current[ 'end_index' ] <= $item->end_index ? $item->end_index : $current[ 'end_index' ];
498
+ }
499
+ }
500
+ }
501
+
502
+
503
+ if ( $empty ) {
504
+
505
+ $events[ $i ][ 'events' ][] = array(
506
+ 'id' => false,
507
+ 'start_index' => $row_index,
508
+ 'end_index' => $row_index,
509
+ 'column_id' => $column_id,
510
+ 'event' => true,
511
+ );
512
+
513
+ $events[ $i ][ 'count' ] = $default_count;
514
+ $events[ $i ][ 'column_id' ] = $column_id;
515
+ $events[ $i ][ 'hide' ] = false;
516
+
517
+ }
518
+ unset( $current );
519
+ $i ++;
520
+ }
521
+
522
+ $events = sort_events_data_by_order( $events );
523
+
524
+ return array( $events, $column_events );
525
+ }
526
+
527
+ /**
528
+ * Sort events by order
529
+ *
530
+ * @param $events
531
+ *
532
+ * @return mixed
533
+ */
534
+ function sort_events_data_by_order( $events ) {
535
+ foreach ( $events as $cell => $event_data ) {
536
+ usort( $event_data[ 'events' ], function ( $a, $b ) {
537
+ if ( $a[ 'order' ] == $b[ 'order' ] ) {
538
+ return 0;
539
+ }
540
+
541
+ return ( $a[ 'order' ] < $b[ 'order' ] ) ? - 1 : 1;
542
+ } );
543
+ $events[ $cell ][ 'events' ] = $event_data[ 'events' ];
544
+ }
545
+
546
+ return $events;
547
+ }
548
+
549
+ /**
550
+ * Get columns events
551
+ *
552
+ * @param $mptt_shortcode_data
553
+ *
554
+ * @param $post
555
+ *
556
+ * @return array
557
+ */
558
+ function mptt_get_columns_events( $mptt_shortcode_data, $post ) {
559
+ if ( $post === 'all' ) {
560
+ $column_events = $mptt_shortcode_data[ 'events_data' ][ 'column_events' ];
561
+
562
+ return $column_events;
563
+ } else {
564
+ $column_events = array();
565
+
566
+ foreach ( $mptt_shortcode_data[ 'events_data' ][ 'column_events' ] as $col_id => $col_events ) {
567
+ $column_events[ $col_id ] = array_filter(
568
+ $col_events,
569
+ function ( $val ) use ( $post ) {
570
+ return $post->ID == $val->event_id;
571
+ } );
572
+ }
573
+
574
+ return $column_events;
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Checking identical event in cell
580
+ *
581
+ * @param $needle
582
+ * @param $events
583
+ *
584
+ * @return bool
585
+ */
586
+ function mptt_check_exists_column( $needle, $events ) {
587
+ $exist = false;
588
+ $const_available_difference = 1;
589
+
590
+ foreach ( $events as $key => $event ) {
591
+ $difference_data = array_diff( $needle, $event );
592
+ if ( isset( $difference_data[ 'id' ] ) && ( count( $difference_data ) === $const_available_difference ) ) {
593
+ $exist = true;
594
+ break;
595
+ }
596
+ }
597
+
598
+ return $exist;
599
+ }
600
+
601
+ /**
602
+ * Group event in row
603
+ *
604
+ * @param $events_row_data
605
+ *
606
+ * @return array|mixed
607
+ */
608
+ function mptt_group_identical_row_events( $events_row_data ) {
609
+
610
+ $length = count( $events_row_data );
611
+
612
+ for ( $i = 0; $i < $length - 1; $i ++ ) {
613
+ if ( ! isset( $events_row_data[ ( $i + 1 ) ] ) ) {
614
+ continue;
615
+ }
616
+
617
+ $events_current_data = $events_row_data[ $i ];
618
+ $events_current_count = count( $events_current_data[ 'events' ] );
619
+
620
+ $events_next_data = $events_row_data[ ( $i + 1 ) ];
621
+ $events_next_count = count( $events_next_data[ 'events' ] );
622
+
623
+
624
+ if ( $events_next_count > 1 || $events_current_count > 1 ) {
625
+ continue;
626
+ } else {
627
+ if ( filter_var( $events_current_data[ 'events' ][ 0 ][ 'id' ], FILTER_VALIDATE_INT ) && filter_var( $events_next_data[ 'events' ][ 0 ][ 'id' ], FILTER_VALIDATE_INT ) ) {
628
+
629
+ $hash_current = $events_current_data[ 'events' ][ 0 ][ 'hash' ];
630
+ $hash_next = $events_next_data[ 'events' ][ 0 ][ 'hash' ];
631
+
632
+ if ( ! isset( $current_data ) ) {
633
+ $current_data = array( 'key' => $i, 'hash' => $hash_current );
634
+ }
635
+
636
+ if ( $hash_current === $hash_next ) {
637
+
638
+ if ( $current_data[ 'hash' ] !== $hash_current ) {
639
+ $current_data = array( 'key' => $i, 'hash' => $hash_current );
640
+ }
641
+
642
+ if ( $current_data[ 'key' ] === $i ) {
643
+ $events_current_data[ 'count' ] ++;
644
+ $events_current_data[ 'grouped' ] = true;
645
+ $events_row_data[ $i ] = $events_current_data;
646
+ $events_row_data[ $i + 1 ][ 'hide' ] = true;
647
+ } else {
648
+ $events_row_data[ $current_data[ 'key' ] ][ 'count' ] ++;
649
+ $events_row_data[ $i + 1 ][ 'hide' ] = true;
650
+ }
651
+
652
+ }
653
+ }
654
+ }
655
+ }
656
+
657
+ return $events_row_data;
658
+ }
659
+
660
+ /**
661
+ * Check exists grouped attribute
662
+ *
663
+ * @param $event_item
664
+ *
665
+ * @return string
666
+ */
667
+ function mptt_is_grouped_event_class( $event_item ) {
668
+ return ( isset( $event_item[ 'grouped' ] ) && $event_item[ 'grouped' ] ) ? 'mptt-grouped-event' : '';
669
+ }
670
+
671
+ /**
672
+ * Grouped by column
673
+ *
674
+ * @param $events
675
+ *
676
+ * @return array
677
+ */
678
+ function grouped_by_column( $events ) {
679
+ $data = array();
680
+
681
+ foreach ( $events as $key => $event ) {
682
+ if ( ! $event[ 'event' ] ) {
683
+ $data[] = $event;
684
+ continue;
685
+ }
686
+ if ( isset( $data[ $event[ 'column_id' ] ] ) ) {
687
+ $data[ $event[ 'column_id' ] ][ 'related_by_column' ][] = $event;
688
+ } else {
689
+ $data[ $event[ 'column_id' ] ] = $event;
690
+ }
691
+ }
692
+
693
+ return $data;
694
+ }
templates-functions/action-widget-functions.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php use mp_timetable\classes\models\Settings;
2
+
3
+ function mptt_widget_template_before_content() {
4
+ $wrapper_class = mptt_popular_theme_class();
5
+ if (Settings::get_instance()->is_plugin_template_mode()) {
6
+ ?>
7
+ <div class="<?php echo apply_filters('mptt_widget_wrapper_class', 'upcoming-events-widget' . $wrapper_class) ?>">
8
+ <ul class="mptt-widget <?php echo apply_filters('mptt_events_list_class', 'events-list') ?>">
9
+ <?php
10
+ } else {
11
+ ?>
12
+ <div class="widget_recent_entries <?php echo apply_filters('mptt_widget_theme_wrapper_class', 'theme-upcoming-events-widget' . $wrapper_class) ?>">
13
+ <ul class="mptt-widget <?php echo apply_filters('mptt_events_list_class', '') ?>">
14
+ <?php
15
+ }
16
+ }
17
+
18
+ function mptt_widget_template_after_content() { ?>
19
+ </ul>
20
+ </div>
21
+ <?php if(Settings::get_instance()->is_plugin_template_mode()):?>
22
+ <div class="mptt-clearfix"></div><?php
23
+ endif;
24
+ }
25
+
26
+ function mptt_widget_template_content() {
27
+ }
28
+
29
+ /**
30
+ * Widget settings
31
+ *
32
+ * @param $params
33
+ *
34
+ * @return array
35
+ */
36
+ function mptt_widget_settings($params) {
37
+
38
+ $params = shortcode_atts(array(
39
+ 'title' => '',
40
+ 'limit' => '3',
41
+ 'view_settings' => 'today',
42
+ 'next_days' => '1',
43
+ 'time_settings' => '',
44
+ 'mp_categories' => '',
45
+ 'custom_url' => '',
46
+ 'disable_url' => '',
47
+ 'background_color' => '',
48
+ 'hover_background_color' => '',
49
+ 'text_color' => '',
50
+ 'hover_text_color' => '',
51
+ 'item_border_color' => '',
52
+ 'hover_item_border_color' => '',
53
+ ), $params);
54
+
55
+ return $params;
56
+ }
templates-functions/actions-mp-event-functions.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php use mp_timetable\classes\models\Settings;
2
+ use mp_timetable\plugin_core\classes\Core;
3
+
4
+ function mptt_event_template_content_title() { ?>
5
+ <h1 class="event-title"><?php the_title() ?></h1>
6
+ <?php
7
+ }
8
+
9
+ function mptt_event_template_content_thumbnail() { ?>
10
+ <div class="thumbnail-wrapper">
11
+ <?php if (has_post_thumbnail()) {
12
+ the_post_thumbnail(apply_filters('mptt_event_template_content_thumbnail_size', 'large'), array('class' => "event-thumbnail"));
13
+ } ?>
14
+ </div>
15
+ <?php
16
+ }
17
+
18
+ function mptt_event_template_content_post_content() { ?>
19
+ <div class="event-content"><?php the_content(); ?></div>
20
+ <?php
21
+ }
22
+
23
+ function mptt_event_template_content_time_title() {
24
+ $count = count(mptt_get_event_data());
25
+ if (!empty($count)) {
26
+ ?>
27
+ <h3 class="timeslots-title"><?php printf(__('Event Timeslots (%s)', 'mp-timetable'), $count); ?></h3>
28
+ <?php
29
+ }
30
+ }
31
+
32
+ function mptt_event_template_content_time_list() {
33
+ $events = mptt_get_event_data();
34
+ do_action('mptt-before-timeslots', $events);
35
+ ?>
36
+ <ul class="mptt-event <?php echo apply_filters('mptt_events_list_class', 'events-list') ?>">
37
+ <?php foreach ($events as $event): ?>
38
+ <li class="event mptt-colorized" id="event_hours_<?php echo $event->event_id ?>">
39
+
40
+ <h4 class="event-title">
41
+ <a class="event-link" href="<?php echo get_permalink($event->column_id); ?>" title="<?php the_title_attribute(array('post' => $event->event_id)); ?>"><?php echo get_the_title($event->column_id); ?></a>
42
+ </h4>
43
+ <p class="timeslot">
44
+ <time datetime="<?php echo $event->event_start; ?>" class="timeslot-start"><?php
45
+ echo date(get_option('time_format'), strtotime($event->event_start)); ?></time><?php
46
+ echo apply_filters('mptt_timeslot_delimiter', ' - '); ?>
47
+ <time datetime="<?php echo $event->event_end; ?>" class="timeslot-end"><?php
48
+ echo date(get_option('time_format'), strtotime($event->event_end)); ?></time>
49
+ </p>
50
+ <?php if (!empty($event->post->sub_title)) { ?>
51
+ <p class="event-subtitle"><?php echo $event->post->sub_title; ?></p>
52
+ <?php } ?>
53
+
54
+ <?php if (!empty($event->description)) { ?>
55
+ <p class="event-description"><?php echo $event->description; ?></p>
56
+ <?php } ?>
57
+ <?php if (!empty($event->user)) { ?>
58
+ <p class="event-user"><a href="<?php echo get_author_posts_url($event->user->ID); ?>" title="<?php echo $event->user->display_name; ?>"><?php echo get_avatar($event->user->ID, apply_filters('mptt-column-user-avatar-size', 32), '', $event->user->display_name) . ' ';
59
+ echo $event->user->display_name ?></a></p>
60
+ <?php } ?>
61
+ </li>
62
+ <?php endforeach; ?>
63
+ </ul>
64
+ <?php
65
+ do_action('mptt-after-timeslots', $events);
66
+ }
67
+
68
+ function mptt_event_template_content_comments() {
69
+ // If comments are open or we have at least one comment, load up the comment template.
70
+ if (comments_open() || get_comments_number()) {
71
+ comments_template();
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Get post event data
77
+ *
78
+ * @return array
79
+ */
80
+ function mptt_get_event_data() {
81
+ global $post;
82
+ $data = Core::get_instance()->get_controller('events')->get_all_event_by_post($post);
83
+ return !empty($data) ? $data : array();
84
+ }
85
+
86
+ function mptt_theme_wrapper_before() {
87
+ $template = get_option('template');
88
+ switch ($template) {
89
+ case 'twentyeleven' :
90
+ echo '<div id="primary"><div id="content" role="main" class="twentyeleven">';
91
+ break;
92
+ case 'twentytwelve' :
93
+ echo '<div id="primary" class="site-content"><div id="content" role="main" class="twentytwelve">';
94
+ break;
95
+ case 'twentythirteen' :
96
+ echo '<div id="primary" class="site-content"><div id="content" role="main" class="entry-content twentythirteen">';
97
+ break;
98
+ case 'twentyfourteen' :
99
+ echo '<div id="primary" class="content-area"><div id="content" role="main" class="site-content twentyfourteen"><div class="tfmp">';
100
+ break;
101
+ case 'twentyfifteen' :
102
+ echo '<div id="primary" role="main" class="content-area twentyfifteen"><div id="main" class="site-main t15mp">';
103
+ break;
104
+ case 'twentysixteen' :
105
+ echo '<div id="primary" class="content-area twentysixteen"><main id="main" class="site-main" role="main">';
106
+ break;
107
+ default :
108
+ echo '<div id="container"><div id="content" role="main">';
109
+ break;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Theme popular class
115
+ * @return string
116
+ */
117
+ function mptt_popular_theme_class() {
118
+ $template = get_option('template');
119
+
120
+ switch ($template) {
121
+ case 'twentyeleven' :
122
+ $class = ' twentyeleven';
123
+ break;
124
+ case 'twentytwelve' :
125
+ $class = ' twentytwelve';
126
+ break;
127
+ case 'twentythirteen' :
128
+ $class = ' twentythirteen';
129
+ break;
130
+ case 'twentyfourteen' :
131
+ $class = ' twentyfourteen';
132
+ break;
133
+ case 'twentyfifteen' :
134
+ $class = ' twentyfifteen';
135
+ break;
136
+ case 'twentysixteen' :
137
+ $class = ' twentysixteen';
138
+ break;
139
+ default :
140
+ $class = '';
141
+ break;
142
+ }
143
+ return $class;
144
+ }
145
+
146
+ /**
147
+ * Filter post class
148
+ *
149
+ * @param $classes
150
+ * @param string $class
151
+ * @param string $post_id
152
+ *
153
+ * @return mixed
154
+ */
155
+ function mptt_post_class($classes, $class = '', $post_id = '') {
156
+
157
+ if (!$post_id || !in_array(get_post_type($post_id), Core::get_instance()->post_types)) {
158
+ return $classes;
159
+ }
160
+ if ('mp-column' == get_post_type($post_id)) {
161
+ $classes[] = 'mp-column-item';
162
+ } elseif ('mp-event' == get_post_type($post_id)) {
163
+ $classes[] = 'mp-event-item';
164
+ }
165
+ if (!is_search() && is_single() && Settings::get_instance()->is_plugin_template_mode()) {
166
+ if (false !== ($key = array_search('hentry', $classes))) {
167
+ unset($classes[$key]);
168
+ }
169
+ }
170
+
171
+ return $classes;
172
+ }
173
+
174
+ function mptt_theme_wrapper_after() {
175
+
176
+ $template = get_option('template');
177
+
178
+ switch ($template) {
179
+ case 'twentyeleven' :
180
+ echo '</div></div>';
181
+ break;
182
+ case 'twentytwelve' :
183
+ echo '</div></div>';
184
+ break;
185
+ case 'twentythirteen' :
186
+ echo '</div></div>';
187
+ break;
188
+ case 'twentyfourteen' :
189
+ echo '</div></div></div>';
190
+ get_sidebar('content');
191
+ break;
192
+ case 'twentyfifteen' :
193
+ echo '</div></div>';
194
+ break;
195
+ case 'twentysixteen' :
196
+ echo '</div></main>';
197
+ break;
198
+ default :
199
+ echo '</div></div>';
200
+ break;
201
+ }
202
+ }
templates/column/metabox-column-options.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <input type="hidden" name="<?php echo Mp_Time_Table::get_plugin_name() . '_noncename' ?>" value="<?php echo wp_create_nonce(Mp_Time_Table::get_plugin_path()) ?>"/>
2
+ <input type="hidden" id="date-format" value="<?php echo get_option('date_format') ?>">
3
+
4
+ <table id="column-options" class="column-options form-table">
5
+
6
+ <tr>
7
+ <td class="column-option">
8
+ <p>
9
+ <input class="option-input" value="simple" type="radio" name="column[column_option]" id="simple_column"
10
+ <?php echo ($post->column_option === 'simple' || empty($post->column_option)) ? 'checked="checked"' : '' ?>">
11
+ <label for="simple_column" class="option-label"><?php _e('Simple Column', 'mp-timetable') ?></label>
12
+ </p>
13
+ </td>
14
+ </tr>
15
+ <tr>
16
+ <td class="column-option">
17
+ <p>
18
+ <input class="option-input" value="weekday" type="radio" name="column[column_option]" id="mp_weekday"
19
+ <?php echo ($post->column_option === 'weekday') ? 'checked="checked"' : '' ?>>
20
+ <label for="mp_weekday" class="option-label"><?php _e('Day of the week', 'mp-timetable') ?></label>
21
+ </p>
22
+ <select class="option-select mp-weekday" name="column[weekday]" <?php echo ($post->column_option != 'weekday') ? 'disabled="disabled"' : '' ?>>
23
+ <option value=""><?php _e('- Select -', 'mp-timetable') ?></option>
24
+ <option value="sunday" <?php selected( $post->weekday, 'sunday' ); ?>><?php _e('Sunday', 'mp-timetable') ?></option>
25
+ <option value="monday" <?php selected( $post->weekday, 'monday' ); ?>><?php _e('Monday', 'mp-timetable') ?></option>
26
+ <option value="tuesday" <?php selected( $post->weekday, 'tuesday' ); ?>><?php _e('Tuesday', 'mp-timetable') ?></option>
27
+ <option value="wednesday" <?php selected( $post->weekday, 'wednesday' ); ?>><?php _e('Wednesday', 'mp-timetable') ?></option>
28
+ <option value="thursday" <?php selected( $post->weekday, 'thursday' ); ?>><?php _e('Thursday', 'mp-timetable') ?></option>
29
+ <option value="friday" <?php selected( $post->weekday, 'friday' ); ?>><?php _e('Friday', 'mp-timetable') ?></option>
30
+ <option value="saturday" <?php selected( $post->weekday, 'saturday' ); ?>><?php _e('Saturday', 'mp-timetable') ?></option>
31
+ </select>
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <td class="column-option">
36
+ <p>
37
+ <input class="option-input" value="date" type="radio" name="column[column_option]" id="mp_date"
38
+ <?php echo ($post->column_option === 'date') ? 'checked="checked"' : '' ?>>
39
+ <label for="mp_date" class="option-label"><?php _e('Date', 'mp-timetable') ?></label>
40
+ </p>
41
+ <div class="column-datepick mp-date">
42
+ <?php
43
+ $datepicker_value = '';
44
+ if ( !empty($post->option_day) ) {
45
+ $datepicker_value = date('d/m/Y', strtotime(str_replace('/', '-', $post->option_day)));
46
+ }
47
+ ?>
48
+ <input id="datepicker" class="option-input" type="text" name="column[option_day]"
49
+ value="<?php echo $datepicker_value ?>"
50
+ <?php echo ($post->column_option != 'date') ? 'disabled="disabled"' : '' ?>
51
+ placeholder="<?php echo date('d/m/Y', current_time( 'timestamp' ) ) ?>">
52
+ </div>
53
+ </td>
54
+ </tr>
55
+
56
+ </table>
templates/events/column-category.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+ foreach ($terms as $term) {
3
+ ?>
4
+ <a href="<?php echo get_term_link($term->term_id) ?>" rel="tag" title="<?php echo $term->name; ?>"><?php echo $term->name ?></a><?php echo ($term !== end($terms)) ? ', ' : '' ?>
5
+ <?php
6
+ }
templates/events/event-data.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <table class="widefat fixed">
2
+ <thead>
3
+ <tr>
4
+ <th><?php _e('Column', 'mp-timetable') ?></th>
5
+ <th><?php _e('Start', 'mp-timetable') ?></th>
6
+ <th><?php _e('End', 'mp-timetable') ?></th>
7
+ <th><?php _e('Description', 'mp-timetable') ?></th>
8
+ <th><?php _e('Head', 'mp-timetable') ?></th>
9
+ <th><?php _e('Actions', 'mp-timetable') ?></th>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ </tbody>
14
+ </table>
15
+ <div class="events-list-wrapper">
16
+ <table id="events-list" class="widefat fixed striped">
17
+ <tbody>
18
+ <?php if (!empty($event_data)): ?>
19
+ <?php foreach ($event_data as $data): ?>
20
+ <tr data-id="<?php echo $data->id ?>">
21
+ <td class="event-column"><?php echo get_the_title($data->column_id); ?></td>
22
+ <td class="event-start"><?php echo date(get_option('time_format'), strtotime($data->event_start)); ?></td>
23
+ <td class="event-end"><?php echo date(get_option('time_format'), strtotime($data->event_end)); ?></td>
24
+ <td class="event-description"><?php echo $data->description ?></td>
25
+ <td class="event-user-id"><?php
26
+ $user = ($data->user_id != '-1') ? get_userdata($data->user_id) : false;
27
+ if ($user) {
28
+ echo $user->display_name;
29
+ } ?>
30
+ </td>
31
+ <td>
32
+ <a class="button icon dashicons-edit edit-event-button" data-id="<?php echo $data->id ?>" title="<?php _e('Edit event in the form below', 'mp-timetable') ?>"></a>
33
+ <a class="button icon dashicons-trash delete-event-button" data-id="<?php echo $data->id ?>" title="<?php _e('Delete', 'mp-timetable') ?>"></a>
34
+ <span class="spinner left"></span>
35
+ </td>
36
+ </tr>
37
+ <?php endforeach; ?>
38
+ <?php endif; ?>
39
+ </tbody>
40
+ </table>
41
+ </div>
templates/events/metabox-event-data.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <input type="hidden" name="<?php echo Mp_Time_Table::get_plugin_name() . '_noncename' ?>" id="eventmeta_noncename" value="<?php echo wp_create_nonce(Mp_Time_Table::get_plugin_path()) ?>"/>
2
+ <input type="hidden" name="events[place]" id="eventmeta_place" value=""/>
3
+ <input type="hidden" name="events[leading_event]" id="eventmeta_leading_event" value="'.<?php wp_create_nonce(plugin_basename(__FILE__)) ?>.'"/>
4
+ <input type="hidden" id="time_format" value="<?php echo $date["time_format"]["am_pm"] === true ? '1' : '0' ?>"/>
5
+
6
+ <?php \mp_timetable\plugin_core\classes\View::get_instance()->render_html('events/event-data', array('event_data' => $event_data), true) ?>
7
+
8
+ <table id="add_event_table" class="widefat">
9
+ <tr>
10
+ <td><label for="weekday_id"><?php _e('Column:', 'mp-timetable') ?></label></td>
11
+ <td>
12
+ <?php if (count($columns)) { ?>
13
+ <select id="weekday_id" name="events[weekday_id]">
14
+ <?php foreach ($columns as $column) { ?>
15
+ <option value="<?php echo $column->ID ?>"><?php echo $column->post_title ?></option>
16
+ <?php } ?>
17
+ </select>
18
+ <span class="description"><?php printf(__('Select column or <a target="_blank" href="%s">Add New</a>.', 'mp-timetable'), admin_url('post-new.php?post_type=mp-column') ); ?></span>
19
+ <?php } else {
20
+ printf(__('No columns found. <a href="%s">Create at least one column first.</a>', 'mp-timetable'), admin_url('post-new.php?post_type=mp-column'));
21
+ }
22
+ ?>
23
+ </td>
24
+ </tr>
25
+ <tr>
26
+ <td><label for="event_start"><?php _e('Start Time:', 'mp-timetable') ?></label></td>
27
+ <td>
28
+ <input id="event_start" type="text" value="" name="events[start_hour]" maxlength="5" size="5">
29
+ <span class="description"><?php _e('hh:mm', 'mp-timetable') ?></span>
30
+ </td>
31
+ </tr>
32
+ <tr>
33
+ <td><label for="event_end"><?php _e('End Time:', 'mp-timetable') ?></label></td>
34
+ <td>
35
+ <input id="event_end" type="text" value="" name="events[end_hour]" maxlength="5" size="5">
36
+ <span class="description"><?php _e('hh:mm', 'mp-timetable') ?></span>
37
+ </td>
38
+ </tr>
39
+ <tr>
40
+ <td><label for="description"><?php _e('Description:', 'mp-timetable') ?></label></td>
41
+ <td><textarea id="description" class="widefat" name="events[description]" rows="2"></textarea></td>
42
+ </tr>
43
+ <tr>
44
+ <td><label for="user_id"><?php _e('Event Head:', 'mp-timetable') ?></label></td>
45
+ <td>
46
+ <?php
47
+
48
+ global $wp_version;
49
+ $wp_dropdown_users_show = (version_compare($wp_version, '4.5', '<')) ? 'user_login' : 'display_name_with_login';
50
+
51
+ wp_dropdown_users(array(
52
+ 'show_option_none' => __('none', 'mp-timetable'),
53
+ 'show_option_all' => null,
54
+ 'hide_if_only_one_author' => null,
55
+ 'orderby' => 'display_name',
56
+ 'order' => 'ASC',
57
+ 'include' => null,
58
+ 'exclude' => null,
59
+ 'multi' => false,
60
+ 'show' => $wp_dropdown_users_show,
61
+ 'echo' => true,
62
+ 'selected' => false,
63
+ 'include_selected' => false,
64
+ 'name' => 'user_id',
65
+ 'id' => null,
66
+ 'class' => null,
67
+ 'blog_id' => $GLOBALS['blog_id'],
68
+ 'who' => null
69
+ )); ?>
70
+ </td>
71
+ </tr>
72
+ <tr>
73
+ <td></td>
74
+ <td>
75
+ <input id="add_mp_event" type="button" class="button button-primary" value="<?php _e('Add New', 'mp-timetable'); ?>">
76
+ <span class="spinner left"></span>
77
+ </td>
78
+ </tr>
79
+ </table>
templates/events/metabox-event-options.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php ?>
2
+ <table id="add_event_options_table" class="form-table">
3
+ <tr>
4
+ <td><label for="sub_title"><?php _e('Event Subtitle:', 'mp-timetable') ?></label></td>
5
+ <td><input id="sub_title" class="widefat" type="text" value="<?php echo $post->sub_title ?>" name="event_meta[sub_title]"></td>
6
+ </tr>
7
+ <tr class="select-color">
8
+ <td><label for="color"><?php _e('Background Color:', 'mp-timetable'); ?></label></td>
9
+ <td>
10
+ <input type="hidden" class="clr-picker" value="<?php echo $post->color ?>">
11
+ <input type="text" id="color" name="event_meta[color]" value="<?php echo $post->color ?>" data-default-color="transparent">
12
+ </td>
13
+ </tr>
14
+ <tr class="select-color">
15
+ <td><label for="hover_color"><?php _e('Background Hover Color:', 'mp-timetable'); ?></label></td>
16
+ <td>
17
+ <input type="hidden" class="clr-picker" value="<?php echo $post->hover_color ?>">
18
+ <input type="text" id="hover_color" name="event_meta[hover_color]" value="<?php echo $post->hover_color ?>" data-default-color="transparent">
19
+ </td>
20
+ </tr>
21
+ <tr class="select-color">
22
+ <td><label for="text_color"><?php _e('Text Color:', 'mp-timetable'); ?></label></td>
23
+ <td>
24
+ <input type="hidden" class="clr-picker" value="<?php echo $post->text_color ?>">
25
+ <input type="text" id="text_color" name="event_meta[text_color]" value="<?php echo $post->text_color ?>" data-default-color="transparent">
26
+ </td>
27
+ </tr>
28
+ <tr class="select-color">
29
+ <td><label for="hover_text_color"><?php _e('Text Hover Color:', 'mp-timetable'); ?></label></td>
30
+ <td>
31
+ <input type="hidden" class="clr-picker" value="<?php echo $post->hover_text_color ?>">
32
+ <input type="text" id="hover_text_color" name="event_meta[hover_text_color]" value="<?php echo $post->hover_text_color ?>" data-default-color="transparent">
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <td><label for="timetable_custom_url"><?php _e('Custom Event URL:', 'mp-timetable'); ?></label></td>
37
+ <td>
38
+ <input type="text" id="timetable_custom_url" class="widefat" placeholder="http://mywebsite.com" name="event_meta[timetable_custom_url]" value="<?php echo $post->timetable_custom_url ?>">
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <td><label for="select-disable-url"><?php _e('Disable link to this event:', 'mp-timetable'); ?></label></td>
43
+ <td>
44
+ <select id="select-disable-url" name="event_meta[timetable_disable_url]">
45
+ <option value="0" <?php echo !($post->timetable_disable_url) ? 'selected="selected"' : '' ?> ><?php _e('No', 'mp-timetable') ?></option>
46
+ <option value="1" <?php echo ($post->timetable_disable_url) ? 'selected="selected"' : '' ?>><?php _e('Yes', 'mp-timetable') ?></option>
47
+ </select>
48
+ </td>
49
+ </tr>
50
+ </table>
templates/events/no-script.php ADDED
@@ -0,0 +1 @@
 
1
+ <noscript><style type="text/css">.mptt-shortcode-wrapper .mptt-shortcode-table:first-of-type{display:table!important}.mptt-shortcode-wrapper .mptt-shortcode-table .mptt-event-container:hover{height:auto!important;min-height:100%!important}body.mprm_ie_browser .mptt-shortcode-wrapper .mptt-event-container{height:auto!important}@media (max-width:767px){.mptt-shortcode-wrapper .mptt-shortcode-table:first-of-type{display:none!important}}</style></noscript>
templates/popup/index.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form id="mptt-settings" method="post">
2
+ <div class="mptt-shortcode-settings-wrapper">
3
+ <table class="form-table striped">
4
+ <tr>
5
+ <td><label for="weekday"><?php _e('<b>Columns</b> (required)', 'mp-timetable') ?></label></td>
6
+ <td>
7
+ <select multiple="multiple" id="weekday" name="weekday" class="widefat">
8
+ <?php foreach ($data[ 'column' ] as $column): ?>
9
+ <option value="<?php echo $column->ID; ?>"><?php echo $column->post_title; ?></option>
10
+ <?php endforeach; ?>
11
+ </select>
12
+ <p class="description"><?php _e('In order to display multiple points hold ctrl/cmd button.', 'mp-timetable') ?></p>
13
+ </td>
14
+ </tr>
15
+ <tr>
16
+ <td><label for="event"><?php _e('Specific <b>events</b>', 'mp-timetable') ?></label></td>
17
+ <td>
18
+ <select multiple="multiple" id="event" name="event" class="widefat">
19
+ <?php foreach ($data[ 'events' ] as $events): ?>
20
+ <option value="<?php echo $events->ID; ?>"><?php echo $events->post_title; ?></option>
21
+ <?php endforeach; ?>
22
+ </select>
23
+ <p class="description"><?php _e('In order to display multiple points hold ctrl/cmd button.', 'mp-timetable') ?></p>
24
+ </td>
25
+ </tr>
26
+ <tr>
27
+ <td><label for="event_category"><?php _e('Event <b>categories</b>', 'mp-timetable'); ?></label></td>
28
+ <td>
29
+ <select multiple="multiple" id="event_category" name="event_category" class="widefat">
30
+ <?php foreach ($data[ 'category' ] as $category): ?>
31
+ <option value="<?php echo $category->term_id; ?>"><?php echo $category->name; ?></option>
32
+ <?php endforeach; ?>
33
+ </select>
34
+ <p class="description"><?php _e('In order to display multiple points hold ctrl/cmd button.', 'mp-timetable'); ?></p>
35
+ </td>
36
+ </tr>
37
+ <tr>
38
+ <td><label><?php _e('Fields to display:', 'mp-timetable'); ?></label></td>
39
+ <td>
40
+ <label for="title" class="label_width"><input type="checkbox" name="title" checked value="1"/><?php _e('Title', 'mp-timetable'); ?></label><br/>
41
+ <label for="time" class="label_width"><input type="checkbox" name="time" checked value="1"/><?php _e('Time', 'mp-timetable'); ?></label><br/>
42
+ <label for="sub-title" class="label_width"><input type="checkbox" name="sub-title" checked value="1"/><?php _e('Subtitle', 'mp-timetable'); ?></label><br/>
43
+ <label for="description" class="label_width"><input type="checkbox" name="description" value="1"/><?php _e('Description', 'mp-timetable'); ?></label><br/>
44
+ <label for="user" class="label_width"><input type="checkbox" name="user" value="1"/><?php _e('Event Head', 'mp-timetable'); ?></label>
45
+ <p class="description"><?php _e('Check the event parameter(s) to be displayed in the timetable.', 'mp-timetable'); ?></p>
46
+ </td>
47
+ </tr>
48
+ <tr>
49
+ <td><label for="row_height"><?php _e('Block height in pixels', 'mp-timetable'); ?></label></td>
50
+ <td>
51
+ <input type="text" name="row_height" id="row_height" value="45" class="regular-text">
52
+ <p class="description"><?php _e('Set height of the block', 'mp-timetable'); ?></p>
53
+ </td>
54
+ </tr>
55
+ <tr>
56
+ <td><label for="font_size"><?php _e('Base font size', 'mp-timetable'); ?></label></td>
57
+ <td>
58
+ <input type="text" name="font_size" id="font_size" value="" class="regular-text">
59
+ <p class="description"><?php _e('Base font size for the table. Example 12px, 2em, 80%.', 'mp-timetable'); ?></p>
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <td><label for="measure"><?php _e('Time frame for event', 'mp-timetable'); ?></label></td>
64
+ <td>
65
+ <select id="measure" name="measure">
66
+ <option value="1"><?php _e('Hour (1h)', 'mp-timetable'); ?></option>
67
+ <option value="0.5"><?php _e('Half hour (30min)', 'mp-timetable'); ?></option>
68
+ <option value="0.25"><?php _e('Quarter hour (15min)', 'mp-timetable'); ?></option>
69
+ </select>
70
+ </td>
71
+ </tr>
72
+ <tr>
73
+ <td><label for="filter_style"><?php _e('Filter events style', 'mp-timetable'); ?> </label></td>
74
+ <td>
75
+ <select id="filter_style" name="filter_style">
76
+ <option value="dropdown_list"><?php _e('Dropdown', 'mp-timetable'); ?></option>
77
+ <option value="tabs"><?php _e('Tabs', 'mp-timetable'); ?></option>
78
+ <option value="none"><?php _e('None', 'mp-timetable'); ?></option>
79
+ </select>
80
+ </td>
81
+ </tr>
82
+ <tr>
83
+ <td><label for="filter_label"><?php _e('Filter title to display all events', 'mp-timetable'); ?></label></td>
84
+ <td>
85
+ <input type="text" name="filter_label" id="filter_label" value="All Events" class="regular-text">
86
+ </td>
87
+ </tr>
88
+ <tr>
89
+ <td><label for="hide_all_events_view"><?php _e('Hide \'All Events\' option', 'mp-timetable'); ?></label>
90
+ </td>
91
+ <td>
92
+ <select id="hide_all_events_view" name="hide_all_events_view">
93
+ <option value="0"><?php _e('No', 'mp-timetable') ?></option>
94
+ <option value="1"><?php _e('Yes', 'mp-timetable') ?></option>
95
+ </select>
96
+ </td>
97
+ </tr>
98
+ <tr>
99
+ <td><label for="hide_hours_column"><?php _e('Hide column with hours', 'mp-timetable'); ?></label></td>
100
+ <td>
101
+ <select id="hide_hours_column" name="hide_hours_column">
102
+ <option value="0"><?php _e('No', 'mp-timetable') ?></option>
103
+ <option value="1"><?php _e('Yes', 'mp-timetable') ?></option>
104
+ </select>
105
+ </td>
106
+ </tr>
107
+ <tr>
108
+ <td><label for="hide_empty"><?php _e('Do not display empty rows', 'mp-timetable'); ?></label></td>
109
+ <td>
110
+ <select id="hide_empty" name="hide_empty">
111
+ <option value="1"><?php _e('Yes', 'mp-timetable') ?></option>
112
+ <option value="0"><?php _e('No', 'mp-timetable') ?></option>
113
+ </select>
114
+ </td>
115
+ </tr>
116
+ <tr>
117
+ <td><label for="group_events"><?php _e('Merge cells with common events', 'mp-timetable'); ?></label></td>
118
+ <td>
119
+ <select id="group_events" name="group_events">
120
+ <option value="0"><?php _e('No', 'mp-timetable') ?></option>
121
+ <option value="1"><?php _e('Yes', 'mp-timetable') ?></option>
122
+ </select>
123
+ </td>
124
+ </tr>
125
+ <tr>
126
+ <td><label for="disable_event_url"><?php _e('Disable event link', 'mp-timetable'); ?></label></td>
127
+ <td>
128
+ <select id="disable_event_url" name="disable_event_url">
129
+ <option value="0"><?php _e('No', 'mp-timetable') ?></option>
130
+ <option value="1"><?php _e('Yes', 'mp-timetable') ?></option>
131
+ </select>
132
+ </td>
133
+ </tr>
134
+ <tr>
135
+ <td><label for="text_align"><?php _e('Horizontal align', 'mp-timetable') ?> </label></td>
136
+ <td><select id="text_align" name="text_align">
137
+ <option value="center"><?php _e('center', 'mp-timetable') ?></option>
138
+ <option value="left"><?php _e('left', 'mp-timetable') ?></option>
139
+ <option value="right"><?php _e('right', 'mp-timetable') ?></option>
140
+ </select>
141
+ </td>
142
+ </tr>
143
+ <tr>
144
+ <td><label for="text_align_vertical"><?php _e('Vertical align', 'mp-timetable') ?> </label></td>
145
+ <td><select id="text_align_vertical" name="text_align_vertical">
146
+ <option value="default"><?php _e('default', 'mp-timetable') ?></option>
147
+ <option value="top"><?php _e('top', 'mp-timetable') ?></option>
148
+ <option value="middle"><?php _e('middle', 'mp-timetable') ?></option>
149
+ <option value="bottom"><?php _e('bottom', 'mp-timetable') ?></option>
150
+ </select>
151
+ </td>
152
+ </tr>
153
+ <tr>
154
+ <td><label for="id"><?php _e('Unique ID', 'mp-timetable'); ?></label></td>
155
+ <td>
156
+ <input type="text" name="id" id="id" value="" class="regular-text">
157
+ <p class="description"><?php _e('If you use more than one table on a page specify the unique ID for a timetable. It is usually all lowercase and contains only letters, numbers, and hyphens.', 'mp-timetable'); ?></p>
158
+ </td>
159
+ </tr>
160
+ <tr>
161
+ <td><label for="custom_class"><?php _e('CSS class', 'mp-timetable'); ?></label></td>
162
+ <td>
163
+ <input type="text" name="custom_class" id="custom_class" value="" class="regular-text">
164
+ </td>
165
+ </tr>
166
+ <tr>
167
+ <td><label for="responsive"><?php _e('Mobile behavior', 'mp-timetable'); ?></label></td>
168
+ <td>
169
+ <select id="responsive" name="responsive">
170
+ <option value="1"><?php _e('List', 'mp-timetable') ?></option>
171
+ <option value="0"><?php _e('Table', 'mp-timetable') ?></option>
172
+ </select>
173
+ <p class="description"><?php _e('Tick "List" to display events in a list view on mobile devices. Tick "Table" to display events in a table.', 'mp-timetable'); ?></p>
174
+ </td>
175
+ </tr>
176
+ </table>
177
+ </div>
178
+ <div class="mptt-shortcode-submit-wrapper">
179
+ <input type="button" value="<?php _e('Add Timetable', 'mp-timetable'); ?>" id="insert-into" class="button button-primary button-large" name="save">
180
+ </div>
181
+ </form>
templates/settings/general.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h1 class="wp-heading-inline"><?php _e('General Settings', 'mp-timetable'); ?></h1>
3
+
4
+ <?php settings_errors('mpTimetableSettings', false); ?>
5
+
6
+ <form method="POST">
7
+ <table class="form-table">
8
+ <tr>
9
+ <td><label for="template_source"><?php _e('Template Mode', 'mp-timetable'); ?></label></td>
10
+ <td>
11
+ <?php $theme_mode = !empty($settings['theme_mode']) ? $settings['theme_mode'] : 'theme'; ?>
12
+ <select id="theme_mode" name="theme_mode" <?php echo $theme_supports ? ' disabled' : ''; ?>>
13
+ <option value="theme" <?php selected($theme_mode, 'theme'); ?>><?php _e('Theme Mode', 'mp-timetable'); ?></option>
14
+ <option value="plugin" <?php selected($theme_mode, 'plugin'); ?>><?php _e('Developer Mode', 'mp-timetable'); ?></option>
15
+ </select>
16
+ <p class="description"><?php _e("Choose Theme Mode to display the content with the styles of your theme. Choose Developer Mode to control appearance of the content with custom page templates, actions and filters.", 'mp-timetable'); ?><br/><?php _e("This option can't be changed if your theme is initially integrated with the plugin.", 'mp-timetable'); ?></p>
17
+ </td>
18
+ </tr>
19
+ <?php
20
+ if ( apply_filters('mptt_permalinks_enabled', true) ) {
21
+ ?>
22
+ <tr>
23
+ <td><?php _e('Permalink Settings'); ?></td>
24
+ <td><?php echo sprintf( __('Configure permalink settings in <a href="%s">Settings > Permalinks</a>', 'mp-timetable'), admin_url('options-permalink.php') ); ?></td>
25
+ </tr>
26
+ <?php } ?>
27
+ </table>
28
+ <p class="submit">
29
+ <input type="submit" name="submit" id="submit" class="button-primary" value="<?php _e('Save', 'mp-timetable') ?>"/>
30
+ <input type="hidden" name="mp-timetable-save-settings" value="<?php echo wp_create_nonce('mp_timetable_nonce_settings') ?>">
31
+ </p>
32
+ </form>
33
+ </div>
templates/shortcodes/empty-search-events.php ADDED
@@ -0,0 +1 @@
 
1
+ <div class="mptt-notice"><?php _e('no events found', 'mp-timetable'); ?> </div>
templates/shortcodes/event-container.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php $item[ 'post' ] = get_post( $item[ 'event_id' ] ); ?>
2
+ <div data-event-id="<?php echo $item[ 'event_id' ] ?>" data-start="<?php echo empty( $startIndex ) ? $item[ 'start_index' ] : $startIndex ?>" data-start-item="<?php echo $item[ 'start_index' ] ?>"
3
+ data-end="<?php echo $item[ 'end_index' ] ?>"
4
+ class="mptt-event-container id-<?php echo $item[ 'id' ]; ?> mptt-colorized"
5
+ data-type="event"
6
+ data-bg_hover_color="<?php echo $item[ 'post' ]->hover_color ? $item[ 'post' ]->hover_color : '' ?>"
7
+ data-bg_color="<?php echo $item[ 'post' ]->color ? $item[ 'post' ]->color : '' ?>"
8
+ data-hover_color="<?php echo $item[ 'post' ]->hover_text_color ? $item[ 'post' ]->hover_text_color : '' ?>"
9
+ data-color="<?php echo $item[ 'post' ]->text_color ? $item[ 'post' ]->text_color : '' ?>"
10
+ data-min-height=""
11
+ style="<?php echo $params[ 'text_align' ] ? 'text-align:' . $params[ 'text_align' ] . ';' : '' ?>
12
+ <?php echo $item[ 'post' ]->color ? 'background-color:' . $item[ 'post' ]->color . ';' : '' ?>
13
+ <?php echo $item[ 'post' ]->text_color ? 'color:' . $item[ 'post' ]->text_color . ';' : '' ?>
14
+ <?php echo ! empty( $height ) ? 'height:' . $height . '%;' : '' ?>
15
+ <?php echo ! empty( $top ) ? 'top:' . $top . '%;' : '' ?>">
16
+ <div class="mptt-inner-event-content">
17
+ <?php if ( $params[ 'title' ] ) {
18
+ $disable_url = (bool) $item[ 'post' ]->timetable_disable_url || (bool) $params[ 'disable_event_url' ];
19
+
20
+ if ( ! $disable_url ) { ?>
21
+ <a title="<?php echo $item[ 'post' ]->post_title; ?>" href="<?php echo ( $item[ 'post' ]->timetable_custom_url != "" ) ? $item[ 'post' ]->timetable_custom_url : get_permalink( $item[ 'event_id' ] ); ?>" class="event-title"><?php echo $item[ 'post' ]->post_title; ?></a>
22
+ <?php }
23
+
24
+ if ( $disable_url ) { ?>
25
+ <span class="event-title"><?php echo $item[ 'post' ]->post_title; ?></span>
26
+ <?php }
27
+ }
28
+
29
+ if ( $params[ 'time' ] ): ?>
30
+ <p class="timeslot">
31
+ <time datetime="<?php echo $item[ 'event_start' ]; ?>" class="timeslot-start"><?php echo date( get_option( 'time_format' ), strtotime( $item[ 'event_start' ] ) ); ?></time>
32
+ <span class="timeslot-delimiter"><?php echo apply_filters( 'mptt_timeslot_delimiter', ' - ' ); ?></span>
33
+ <time datetime="<?php echo $item[ 'event_end' ]; ?>" class="timeslot-end"><?php echo date( get_option( 'time_format' ), strtotime( $item[ 'event_end' ] ) );; ?></time>
34
+ </p>
35
+ <?php endif;
36
+
37
+ if ( $params[ 'sub-title' ] && ! empty( $item[ 'post' ]->sub_title ) ): ?>
38
+ <p class="event-subtitle"><?php echo $item[ 'post' ]->sub_title; ?></p>
39
+ <?php endif;
40
+
41
+ if ( $params[ 'description' ] && ! empty( $item[ 'description' ] ) ): ?>
42
+ <p class="event-description"><?php echo $item[ 'description' ]; ?></p>
43
+ <?php endif;
44
+
45
+ if ( $params[ 'user' ] && $item[ 'user_id' ] != '-1' ): ?>
46
+ <p class="event-user"><?php $user_info = get_userdata( $item[ 'user_id' ] );
47
+ if ( $user_info ) {
48
+ echo get_avatar( $item[ 'user_id' ], apply_filters( 'mptt-event-user-avatar-size', 24 ), '', $user_info->data->display_name );
49
+ echo $user_info->data->display_name;
50
+ } ?>
51
+ </p>
52
+ <?php endif; ?>
53
+ </div>
54
+ </div>
templates/shortcodes/index-timetable.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ do_action('mptt_shortcode_template_before_content');
4
+
5
+ do_action('mptt_shortcode_template_content');
6
+
7
+ do_action('mptt_shortcode_template_after_content');
8
+
templates/shortcodes/table-header.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <thead>
2
+ <tr class="mptt-shortcode-row">
3
+ <?php foreach ($header_items as $key => $column):
4
+ if (!$column[ 'output' ]) {
5
+ continue;
6
+ } ?>
7
+ <th data-index="<?php echo $key ?>" data-column-id="<?php echo $column[ 'id' ] ?>"><?php echo $column[ 'title' ] ?></th>
8
+ <?php endforeach; ?>
9
+ </tr>
10
+ </thead>
templates/single-mp-column.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php get_header();
2
+
3
+ do_action('mptt-single-mp-column-before-wrapper');
4
+
5
+ do_action('mptt-single-before-wrapper');
6
+
7
+ while (have_posts()) : the_post();
8
+ ?>
9
+ <div <?php post_class(apply_filters('mptt_main_wrapper_class', 'mptt-main-wrapper')) ?>>
10
+ <?php
11
+ /**
12
+ * add_action('mptt_single_column_template_content', 'mptt_column_template_content_title', 10);
13
+ * add_action('mptt_single_column_template_content', 'mptt_column_template_content_post_content', 20);
14
+ * add_action('mptt_single_column_template_content', 'mptt_column_template_content_events_list', 30);
15
+ */
16
+ do_action('mptt_single_column_template_content');
17
+ ?>
18
+ <div class="mptt-clearfix"></div>
19
+ </div>
20
+
21
+ <?php
22
+ endwhile;
23
+
24
+ do_action('mptt_after_main_wrapper'); ?>
25
+ <div class="mptt-clearfix"></div>
26
+ <?php
27
+ do_action('mptt-single-mp-column-after-wrapper');
28
+ get_footer(); ?>
templates/single-mp-event.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php get_header();
2
+
3
+ do_action('mptt-single-mp-event-before-wrapper');
4
+
5
+ do_action('mptt_before_main_wrapper');
6
+
7
+ while (have_posts()) : the_post();
8
+ ?>
9
+ <div <?php post_class(apply_filters('mptt_main_wrapper_class', 'mptt-main-wrapper')) ?>>
10
+ <div class="<?php echo apply_filters('mptt_event_template_content_class', 'mptt-content') ?>">
11
+ <?php
12
+ /**
13
+ * add_action('mptt_event_item_content', 'mptt_event_template_content_title', 10);
14
+ * add_action('mptt_event_item_content', 'mptt_event_template_content_thumbnail', 20);
15
+ * add_action('mptt_event_item_content', 'mptt_event_template_content_post_content', 30);
16
+ * add_action('mptt_event_item_content', 'mptt_event_template_content_title', 40);
17
+ * add_action('mptt_event_item_content', 'mptt_event_template_content_time_list', 50);
18
+ */
19
+ do_action('mptt_event_item_content');
20
+ ?>
21
+ </div>
22
+ <div class="<?php echo apply_filters('mptt_sidebar_class', 'mptt-sidebar') ?>">
23
+ <?php
24
+ do_action('mptt_sidebar');
25
+ ?>
26
+ </div>
27
+ <div class="mptt-clearfix"></div>
28
+ </div>
29
+
30
+ <?php
31
+ endwhile;
32
+
33
+ do_action('mptt_after_main_wrapper');
34
+
35
+ do_action('mptt-single-mp-event-after-wrapper');
36
+
37
+ get_footer(); ?>
templates/taxonomies/taxonomy-link.php ADDED
@@ -0,0 +1 @@
 
1
+ <a rel="tag" href="<?php echo $data["filter_link"]; ?>" title="<?php echo $data["wp"]->name; ?>"><?php echo $data["wp"]->name ?></a>
templates/templates-actions/action-sidebar.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (is_active_sidebar('mptt-sidebar')) {
3
+ dynamic_sidebar('mptt-sidebar');
4
+
5
+ } elseif (get_option('template') != 'twentyfourteen') {
6
+
7
+ the_widget('timetable\classes\widgets\Timetable_widget', apply_filters('mptt_widget_settings', array(
8
+ 'title' => __('Today upcoming events', 'mp-timetable'),
9
+ 'limit' => '3')
10
+ ), array('widget_id' => 'wp-timetable-1' . $post->ID));
11
+
12
+ the_widget('timetable\classes\widgets\Timetable_widget', apply_filters('mptt_widget_settings', array(
13
+ 'title' => __('All upcoming events', 'mp-timetable'),
14
+ 'view_settings' => 'all',
15
+ 'limit' => '10',
16
+ 'next_days' => '5',
17
+ )), array('widget_id' => 'wp-timetable-2' . $post->ID));
18
+
19
+ };
templates/theme/column-events.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $time_format = get_option('time_format');
3
+
4
+ do_action('mptt_column_events_before_events', $events);
5
+
6
+ if ( !empty($events) ) {
7
+
8
+ foreach ($events as $event): ?>
9
+ <p class="event mptt-theme-mode-event" id="event_<?php echo $event->event_id ?>">
10
+
11
+ <?php if (has_post_thumbnail($event->event_id)) {
12
+ echo wp_get_attachment_image( get_post_thumbnail_id($event->event_id), apply_filters('mptt_event_thumbnail_size', 'thumbnail'), false, array('class' => "alignleft event-thumbnail", 'alt' => get_the_title($event->event_id)));
13
+ } else { ?>
14
+ <img class="alignleft event-thumbnail event-thumbnail-default" src="<?php echo \Mp_Time_Table::get_plugin_url() . 'media/css/images/column_icon.png' ?>">
15
+ <?php } ?>
16
+
17
+ <a href="<?php echo $event->post->timetable_disable_url == '1' ? '#' : ($event->post->timetable_custom_url != "" ? $event->post->timetable_custom_url : get_permalink($event->event_id)) ?>" class="event-link">
18
+ <?php echo get_the_title($event->event_id); ?>
19
+ </a>
20
+
21
+ <br/>
22
+
23
+ <time datetime="<?php echo $event->event_start; ?>" class="timeslot-start"><?php echo date($time_format, strtotime($event->event_start)); ?></time><?php echo apply_filters('mptt_timeslot_delimiter', ' - '); ?>
24
+ <time datetime="<?php echo $event->event_end; ?>" class="timeslot-end"><?php echo date($time_format, strtotime($event->event_end)); ?></time>
25
+
26
+ <?php if (!empty($event->post->sub_title)) { ?>
27
+ <br/>
28
+ <span class="event-subtitle"><?php echo $event->post->sub_title ?></span>
29
+ <?php } ?>
30
+
31
+ <?php if (!empty($event->description)) { ?>
32
+ <br/>
33
+ <span class="event-description"><?php echo $event->description; ?></span>
34
+ <?php } ?>
35
+
36
+ <?php if (!empty($event->user)) { ?>
37
+ <br/>
38
+ <span class="event-user vcard">
39
+ <?php echo get_avatar($event->user->ID, apply_filters('mptt_column_events_avatar_size', 32), '', $event->user->display_name); ?>
40
+ <?php echo $event->user->display_name ?>
41
+ </span>
42
+ <?php } ?>
43
+ </p>
44
+ <?php endforeach;
45
+
46
+ }
47
+
48
+ do_action('mptt_column_events_after_events', $events);
templates/theme/event-timeslots.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ do_action('mptt_event_timeslots_before_title');
4
+
5
+ if ( !empty($count) ) {
6
+ ?>
7
+ <h3 class="timeslots-title"><?php printf(__('Event Timeslots (%s)', 'mp-timetable'), $count); ?></h3>
8
+ <?php
9
+ }
10
+ $time_format = get_option('time_format');
11
+
12
+ do_action('mptt_event_timeslots_before_timeslots', $events);
13
+
14
+ if ( !empty($events) ) {
15
+ foreach ($events as $event): ?>
16
+ <p class="timeslot">
17
+
18
+ <a class="timeslot-link" href="<?php echo get_permalink($event->column_id); ?>"><?php echo get_the_title($event->column_id); ?></a>
19
+
20
+ <br/>
21
+ <time datetime="<?php echo $event->event_start; ?>" class="timeslot-start"><?php echo date($time_format, strtotime($event->event_start)); ?></time>
22
+ <?php echo apply_filters('mptt_timeslot_delimiter', ' - '); ?>
23
+ <time datetime="<?php echo $event->event_start; ?>" class="timeslot-end"><?php echo date($time_format, strtotime($event->event_end)); ?></time>
24
+
25
+ <?php if (!empty($event->post->sub_title)) { ?>
26
+ <br/>
27
+ <span class="timeslot-subtitle"><?php echo $event->post->sub_title; ?></span>
28
+ <?php } ?>
29
+
30
+ <?php if (!empty($event->description)) { ?>
31
+ <br/>
32
+ <span class="timeslot-description"><?php echo $event->description; ?></span>
33
+ <?php } ?>
34
+ <?php if (!empty($event->user)) { ?>
35
+ <br/>
36
+ <span class="timeslot-user vcard">
37
+ <?php echo get_avatar($event->user->ID, apply_filters('mptt_event_timeslots_avatar_size', 32), '', $event->user->display_name); ?> <?php echo $event->user->display_name; ?>
38
+ </span>
39
+ <?php } ?>
40
+
41
+ </p>
42
+ <?php endforeach;
43
+ }
44
+
45
+ do_action('mptt_event_timeslots_after_timeslots', $events);
templates/theme/widget-upcoming-view.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php echo $args['before_widget'];
2
+
3
+ if (!empty($instance['title'])) {
4
+ echo $args['before_title'] . $instance['title'] . $args['after_title'];
5
+ }
6
+
7
+ do_action('mptt_widget_upcoming_before_content', $events);
8
+
9
+ $time_format = get_option('time_format');
10
+
11
+ $events_group_by_categories = array();
12
+
13
+ foreach ($events as $event) {
14
+ if (!isset($temp[$event->column_id])) {
15
+ $events_group_by_categories[$event->column_id][] = $event;
16
+ } else {
17
+ $events_group_by_categories[$event->column_id][] = $event;
18
+ }
19
+ }
20
+
21
+ if (!empty($events)): ?>
22
+ <?php foreach ($events_group_by_categories as $key_category => $cat_events) { ?>
23
+ <ul>
24
+ <?php foreach ($cat_events as $key => $event):
25
+ $event_class = 'event';
26
+ ?>
27
+ <li class="<?php echo apply_filters('mptt_widget_upcoming_event_class', $event_class) ?>">
28
+ <?php
29
+
30
+ $disable_url = (bool)$event->post->timetable_disable_url || (bool)$instance['disable_url'];
31
+ $url = ($instance['custom_url'] != "") ? $instance['custom_url'] : (($event->post->timetable_custom_url != "") ? $event->post->timetable_custom_url : get_permalink($event->event_id));
32
+
33
+ if (!$disable_url) { ?>
34
+ <a href="<?php echo $url ?>" title="<?php echo get_the_title($event->event_id) ?>" class="event-link">
35
+ <?php }
36
+ echo get_the_title($event->event_id);
37
+ if (!$disable_url) { ?>
38
+ </a><br/>
39
+ <?php } ?>
40
+ <span class="post-date">
41
+ <?php if ($instance['view_settings'] !== 'today' && $instance['view_settings'] !== 'current'): ?><?php echo get_the_title($event->column_id) ?>
42
+ <br/>
43
+ <?php endif; ?>
44
+ <time datetime="<?php echo $event->event_start; ?>" class="timeslot-start"><?php echo date($time_format, strtotime($event->event_start)); ?></time>
45
+ <?php echo apply_filters('mptt_timeslot_delimiter', ' - '); ?>
46
+ <time datetime="<?php echo $event->event_end; ?>" class="timeslot-end"><?php echo date($time_format, strtotime($event->event_end)); ?></time>
47
+ </span>
48
+ </li>
49
+ <?php endforeach; ?>
50
+ </ul>
51
+ <?php
52
+ }
53
+ else:
54
+ _e('no events found', 'mp-timetable');
55
+ endif;
56
+
57
+ do_action('mptt_widget_upcoming_after_content', $events);
58
+
59
+ echo $args['after_widget'] ?>
templates/widgets/gallery-list.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="<?php echo $widget_object->widget_options['classname'] ?>">
2
+ <p>
3
+ <label for="<?php echo $widget_object->get_field_id('title') ?>"><?php _e('Title', "mp-timetable") ?></label>
4
+ <input class="widefat" id="<?php echo $widget_object->get_field_id('title') ?>"
5
+ name="<?php echo $widget_object->get_field_name('title') ?>" type="text"
6
+ value="<?php echo $instance['title'] ?>">
7
+ </p>
8
+ <p>
9
+ <label
10
+ for="<?php echo $widget_object->get_field_id('view_settings') ?>"><?php _e('Events to display', "mp-timetable") ?></label>
11
+ <select class="view_settings widefat" id="<?php echo $widget_object->get_field_id('view_settings') ?>"
12
+ name="<?php echo $widget_object->get_field_name('view_settings') ?>">
13
+ <option
14
+ value="today" <?php echo $instance['view_settings'] === 'today' ? 'selected="selected"' : '' ?> ><?php _e('Today upcoming events', "mp-timetable") ?></option>
15
+ <option
16
+ value="all" <?php echo $instance['view_settings'] === 'all' ? 'selected="selected"' : '' ?> ><?php _e('All upcoming events', "mp-timetable") ?></option>
17
+ <option
18
+ value="current"<?php echo $instance['view_settings'] === 'current' ? 'selected="selected"' : '' ?>><?php _e('Ongoing events', "mp-timetable") ?></option>
19
+ </select>
20
+ </p>
21
+ <p class="next-days" style="display: <?php echo $instance['view_settings'] === 'all' ? 'block' : 'none' ?> ;">
22
+ <label for="<?php echo $widget_object->get_field_id('next_days') ?>">
23
+ <?php _e('Input number of days', "mp-timetable") ?>
24
+ </label>
25
+ <input class="widefat" id="<?php echo $widget_object->get_field_id('next_days') ?>"
26
+ name="<?php echo $widget_object->get_field_name('next_days') ?>" type="text"
27
+ value="<?php echo $instance['next_days'] ?>">
28
+ <?php _n('day', 'days', $instance['next_days'], "mp-timetable") ?>
29
+ </p>
30
+ <p>
31
+ <label class="widget-categories"
32
+ for="<?php echo $widget_object->get_field_id('mp_categories') ?>"><?php _e('Event categories. Leave blank to display all.', "mp-timetable") ?></label>
33
+ <select class="widefat" multiple="multiple" id="<?php echo $widget_object->get_field_id('mp_categories') ?>"
34
+ name="<?php echo $widget_object->get_field_name('mp_categories') ?>[]">
35
+ <?php
36
+ if (!empty($data['categories'])):
37
+ foreach ($data['categories'] as $category) :
38
+ if (empty($instance['mp_categories'])):
39
+ $instance['mp_categories'] = array();
40
+ endif ?>
41
+ <option
42
+ value="<?php echo $category->term_id ?>"<?php echo in_array($category->term_id, $instance['mp_categories']) ? 'selected="selected"' : '' ?> ><?php echo $category->name ?></option>
43
+ <?php endforeach;
44
+ endif; ?>
45
+ </select>
46
+ </p>
47
+ <p>
48
+ <label
49
+ for="<?php echo $widget_object->get_field_id('limit') ?>"><?php _e('Number of events to display', "mp-timetable") ?></label>
50
+ <input class="widefat" id="<?php echo $widget_object->get_field_id('limit') ?>"
51
+ name="<?php echo $widget_object->get_field_name('limit') ?>" type="text"
52
+ value="<?php echo $instance['limit'] ?>">
53
+ </p>
54
+ <p>
55
+ <label
56
+ for="<?php echo $widget_object->get_field_id('disable_url') ?>"><?php _e('Disable event link', "mp-timetable") ?></label>
57
+ <select class="widefat" id="<?php echo $widget_object->get_field_id('disable_url') ?>"
58
+ name="<?php echo $widget_object->get_field_name('disable_url') ?>">
59
+ <option
60
+ value="0" <?php echo $instance['disable_url'] === '0' ? 'selected="selected"' : '' ?>> <?php _e('No', "mp-timetable") ?> </option>
61
+ <option
62
+ value="1" <?php echo $instance['disable_url'] === '1' ? 'selected="selected"' : '' ?>> <?php _e('Yes', "mp-timetable") ?> </option>
63
+ </select>
64
+ </p>
65
+ <p>
66
+ <label
67
+ for="<?php echo $widget_object->get_field_id('custom_url') ?>"><?php _e('Custom link for events', "mp-timetable") ?></label>
68
+ <input class="widefat" id="<?php echo $widget_object->get_field_id('custom_url') ?>"
69
+ name="<?php echo $widget_object->get_field_name('custom_url') ?>" type="text"
70
+ value="<?php echo $instance['custom_url'] ?>">
71
+ </p>
72
+ <?php if (\mp_timetable\classes\models\Settings::get_instance()->is_plugin_template_mode()): ?>
73
+ <p style="margin-bottom:0px;">
74
+ <label
75
+ for="<?php echo $widget_object->get_field_id('background_color'); ?>"><?php _e('Event background color', "mp-timetable"); ?></label>
76
+ </p>
77
+ <p class="select-color" style="margin-top:0px;">
78
+ <input type="hidden" class="clr-picker" value="<?php echo $instance['background_color']; ?>">
79
+ <input class="regular-text" id="<?php echo $widget_object->get_field_id('background_color'); ?>"
80
+ name="<?php echo $widget_object->get_field_name('background_color'); ?>" type="text"
81
+ value="<?php echo $instance['background_color']; ?>"/>
82
+ </p>
83
+ <p style="margin-bottom:0px;">
84
+ <label
85
+ for="<?php echo $widget_object->get_field_id('hover_background_color'); ?>"><?php _e('Event background color on hover', "mp-timetable"); ?></label>
86
+ </p>
87
+ <p class="select-color" style="margin-top:0px;">
88
+ <input type="hidden" class="clr-picker" value="<?php echo $instance['hover_background_color']; ?>">
89
+ <input class="regular-text" id="<?php echo $widget_object->get_field_id('hover_background_color'); ?>"
90
+ name="<?php echo $widget_object->get_field_name('hover_background_color'); ?>" type="text"
91
+ value="<?php echo $instance['hover_background_color']; ?>"/>
92
+ </p>
93
+ <p style="margin-bottom:0px;">
94
+ <label
95
+ for="<?php echo $widget_object->get_field_id('text_color'); ?>"><?php _e('Event text color', "mp-timetable"); ?></label>
96
+ </p>
97
+ <p class="select-color" style="margin-top:0px;">
98
+ <input type="hidden" class="clr-picker" value="<?php echo $instance['text_color']; ?>">
99
+ <input class="regular-text" id="<?php echo $widget_object->get_field_id('text_color'); ?>"
100
+ name="<?php echo $widget_object->get_field_name('text_color'); ?>" type="text"
101
+ value="<?php echo $instance['text_color']; ?>"/>
102
+ </p>
103
+ <p style="margin-bottom:0px;">
104
+ <label
105
+ for="<?php echo $widget_object->get_field_id('hover_text_color'); ?>"><?php _e('Event text color on hover', "mp-timetable"); ?></label>
106
+ </p>
107
+ <p class="select-color" style="margin-top:0px;">
108
+ <input type="hidden" class="clr-picker" value="<?php echo $instance['hover_text_color']; ?>">
109
+ <input class="regular-text" id="<?php echo $widget_object->get_field_id('hover_text_color'); ?>"
110
+ name="<?php echo $widget_object->get_field_name('hover_text_color'); ?>" type="text"
111
+ value="<?php echo $instance['hover_text_color']; ?>"/>
112
+ </p>
113
+ <p style="margin-bottom:0px;">
114
+ <label
115
+ for="<?php echo $widget_object->get_field_id('item_border_color'); ?>"><?php _e('Event border color', "mp-timetable"); ?></label>
116
+ </p>
117
+ <P class="select-color" style="margin-top:0px;">
118
+ <input type="hidden" class="clr-picker" value="<?php echo $instance['item_border_color']; ?>">
119
+ <input class="regular-text" id="<?php echo $widget_object->get_field_id('item_border_color'); ?>"
120
+ name="<?php echo $widget_object->get_field_name('item_border_color'); ?>" type="text"
121
+ value="<?php echo $instance['item_border_color']; ?>"/>
122
+ </P>
123
+ <p style="margin-bottom:0px;">
124
+ <label
125
+ for="<?php echo $widget_object->get_field_id('hover_item_border_color'); ?>"><?php _e('Event border color on hover', "mp-timetable"); ?></label>
126
+ </p>
127
+ <p class="select-color" style="margin-top:0px;">
128
+ <input type="hidden" class="clr-picker" value="<?php echo $instance['hover_item_border_color']; ?>">
129
+ <input class="regular-text" id="<?php echo $widget_object->get_field_id('hover_item_border_color'); ?>"
130
+ name="<?php echo $widget_object->get_field_name('hover_item_border_color'); ?>" type="text"
131
+ value="<?php echo $instance['hover_item_border_color']; ?>"/>
132
+ </p>
133
+ <?php else: ?>
134
+ <input type="hidden" name="<?php echo $widget_object->get_field_name('background_color'); ?>" value=""/>
135
+ <input type="hidden" name="<?php echo $widget_object->get_field_name('hover_background_color'); ?>" value=""/>
136
+ <input type="hidden" name="<?php echo $widget_object->get_field_name('text_color'); ?>" value=""/>
137
+ <input type="hidden" name="<?php echo $widget_object->get_field_name('hover_text_color'); ?>" value=""/>
138
+ <input type="hidden" name="<?php echo $widget_object->get_field_name('item_border_color'); ?>" value=""/>
139
+ <input type="hidden" name="<?php echo $widget_object->get_field_name('hover_item_border_color'); ?>" value=""/>
140
+ <?php endif; ?>
141
+ </div>
142
+ <script type="application/javascript">
143
+ (function($) {
144
+ "use strict";
145
+ $(document).ready(function() {
146
+ Registry._get("Event").initColorPicker('#widgets-right .mptt-container');
147
+ Registry._get("Event").displaySettings("<?php echo $widget_object->get_field_id('view_settings') ?>");
148
+ Registry._get("Event").timeMode("<?php echo $widget_object->get_field_id('time_settings');?>");
149
+ });
150
+ })(jQuery);
151
+ </script>
templates/widgets/widget-view.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php echo $args['before_widget'] ?>
2
+
3
+ <?php use mp_timetable\classes\models\Events as Events;
4
+
5
+ if (!empty($instance['title'])) {
6
+ echo $args['before_title'] . $instance['title'] . $args['after_title'];
7
+ }
8
+
9
+ do_action('mptt_widget_template_before_content', $events);
10
+
11
+ $events_group_by_categories = array();
12
+
13
+ foreach ($events as $event) {
14
+ if (!isset($temp[$event->column_id])) {
15
+ $events_group_by_categories[$event->column_id][] = $event;
16
+ } else {
17
+ $events_group_by_categories[$event->column_id][] = $event;
18
+ }
19
+ }
20
+
21
+ if (!empty($events)): ?>
22
+ <?php foreach ($events_group_by_categories as $key_category => $cat_events) { ?>
23
+ <ul>
24
+ <?php foreach ($cat_events as $key => $event):
25
+ $widget = false;
26
+ $background_color = Events::get_instance()->choose_event_color(array('event_color' => $event->post->color, 'widget_color' => $instance['background_color']));
27
+ $background_hover_color = Events::get_instance()->choose_event_color(array('event_color' => $event->post->hover_color, 'widget_color' => $instance['hover_background_color']));
28
+ $color = Events::get_instance()->choose_event_color(array('event_color' => $event->post->text_color, 'widget_color' => $instance['text_color']));
29
+ $hover_color = Events::get_instance()->choose_event_color(array('event_color' => $event->post->hover_text_color, 'widget_color' => $instance['hover_text_color']));
30
+ $style = 'style="';
31
+
32
+ if ($instance['background_color'] != ''
33
+ || $instance['hover_background_color'] != ''
34
+ || $instance['text_color'] != ''
35
+ || $instance['hover_text_color'] != ''
36
+ || $instance['item_border_color'] != ''
37
+ || $instance['hover_item_border_color'] !== ''
38
+ ) {
39
+ $widget = true;
40
+ }
41
+
42
+ $event_class = 'event' . ($widget ? ' mptt-colorized' : '');
43
+ ?>
44
+
45
+ <li class="<?php echo apply_filters('mptt_widget_upcoming_event_element', $event_class) ?>"
46
+ <?php if ($widget): ?> data-type="widget"
47
+ data-background-color="<?php echo $background_color ?>"
48
+ data-background-hover-color="<?php echo $background_hover_color ?>"
49
+ data-color="<?php echo $color ?>"
50
+ data-hover-color="<?php echo $hover_color ?>"
51
+ data-border-color="<?php echo $instance['item_border_color'] ?>"
52
+ data-hover-border-color="<?php echo $instance['hover_item_border_color'] ?>"
53
+ <?php
54
+ $style .= !empty($instance['item_border_color']) ? ' border-left-color:' . $instance['item_border_color'] . ' ;' : '';
55
+ $style .= !empty($background_color) ? ' background:' . $background_color . ' ;' : '';
56
+ $style .= !empty($color) ? ' color:' . $color . ' ;' : '';
57
+
58
+ else:
59
+ $style .= !empty($event->post->color) ? ' border-left-color:' . $event->post->color . ' ;' : '';
60
+ endif;
61
+
62
+ echo $style . '"';
63
+ ?>>
64
+
65
+ <?php
66
+ $disable_url = (bool)$event->post->timetable_disable_url || (bool)$instance['disable_url'];
67
+ $url = ($instance['custom_url'] != "") ? $instance['custom_url'] : (($event->post->timetable_custom_url != "") ? $event->post->timetable_custom_url : get_permalink($event->event_id)); ?>
68
+ <h4 class="event-title">
69
+ <?php if (!$disable_url) { ?>
70
+ <a href="<?php echo $url ?>" title="<?php echo get_the_title($event->event_id) ?>" class="event-link">
71
+ <?php }
72
+ echo get_the_title($event->event_id);
73
+ if (!$disable_url) { ?>
74
+ </a>
75
+ <?php } ?>
76
+
77
+ </h4>
78
+ <?php if ($instance['view_settings'] !== 'today'): ?><p class="column-title"><?php echo get_the_title($event->column_id) ?></p><?php endif; ?>
79
+
80
+ <p class="timeslot">
81
+ <span class="timeslot-start"><?php echo date(get_option('time_format'), strtotime($event->event_start)); ?></span><?php echo apply_filters('mptt_timeslot_delimiter', ' - '); ?><span class="timeslot-end"><?php echo date(get_option('time_format'), strtotime($event->event_end)); ?>
82
+ </p>
83
+
84
+ </li>
85
+
86
+ <?php endforeach; ?>
87
+ </ul>
88
+ <?php
89
+ }
90
+ else:
91
+ _e('no events found', 'mp-timetable');
92
+ endif;
93
+
94
+ do_action('mptt_widget_template_after_content', $events); ?>
95
+
96
+ <?php echo $args['after_widget'] ?>