Download Monitor - Version 1.0.0

Version Description

  • Complete rewrite of the plugin making use of custom post types and other best practices. Fresh start version '1' to prevent auto-updates (legacy importer needs to be used to migrate from old versions).

=

Download this release

Release Info

Developer jolley_small
Plugin Icon 128x128 Download Monitor
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (114) hide show
  1. assets/css/admin.css +588 -0
  2. assets/css/admin.less +523 -0
  3. assets/css/frontend.css +1 -0
  4. assets/css/frontend.less +137 -0
  5. assets/images/ajax-loader.gif +0 -0
  6. assets/images/cross.png +0 -0
  7. assets/images/download_count_head.png +0 -0
  8. assets/images/downloads-icon.gif +0 -0
  9. assets/images/featured_head.png +0 -0
  10. assets/images/filetypes/document-code.png +0 -0
  11. assets/images/filetypes/document-excel.png +0 -0
  12. assets/images/filetypes/document-film.png +0 -0
  13. assets/images/filetypes/document-flash-movie.png +0 -0
  14. assets/images/filetypes/document-illustrator.png +0 -0
  15. assets/images/filetypes/document-image.png +0 -0
  16. assets/images/filetypes/document-music.png +0 -0
  17. assets/images/filetypes/document-pdf.png +0 -0
  18. assets/images/filetypes/document-photoshop.png +0 -0
  19. assets/images/filetypes/document-powerpoint.png +0 -0
  20. assets/images/filetypes/document-text-image.png +0 -0
  21. assets/images/filetypes/document-text.png +0 -0
  22. assets/images/filetypes/document-word-text.png +0 -0
  23. assets/images/filetypes/document-zipper.png +0 -0
  24. assets/images/filetypes/document.png +0 -0
  25. assets/images/filetypes/readme.txt +63 -0
  26. assets/images/folder-horizontal-open.png +0 -0
  27. assets/images/folder-horizontal.png +0 -0
  28. assets/images/media-button-download.gif +0 -0
  29. assets/images/member_head.png +0 -0
  30. assets/images/menu_icon.png +0 -0
  31. assets/images/on.png +0 -0
  32. assets/images/placeholder.png +0 -0
  33. assets/js/blockui.js +499 -0
  34. assets/js/blockui.min.js +13 -0
  35. assets/js/chosen/chosen-sprite.png +0 -0
  36. assets/js/chosen/chosen-sprite@2x.png +0 -0
  37. assets/js/chosen/chosen.css +413 -0
  38. assets/js/chosen/chosen.jquery.js +1090 -0
  39. assets/js/chosen/chosen.jquery.min.js +10 -0
  40. assets/js/jqueryFileTree/connectors/jqueryFileTree.php +66 -0
  41. assets/js/jqueryFileTree/connectors/jqueryFileTreeDir.php +60 -0
  42. assets/js/jqueryFileTree/images/application.png +0 -0
  43. assets/js/jqueryFileTree/images/code.png +0 -0
  44. assets/js/jqueryFileTree/images/css.png +0 -0
  45. assets/js/jqueryFileTree/images/db.png +0 -0
  46. assets/js/jqueryFileTree/images/directory.png +0 -0
  47. assets/js/jqueryFileTree/images/doc.png +0 -0
  48. assets/js/jqueryFileTree/images/file.png +0 -0
  49. assets/js/jqueryFileTree/images/film.png +0 -0
  50. assets/js/jqueryFileTree/images/flash.png +0 -0
  51. assets/js/jqueryFileTree/images/folder_open.png +0 -0
  52. assets/js/jqueryFileTree/images/html.png +0 -0
  53. assets/js/jqueryFileTree/images/java.png +0 -0
  54. assets/js/jqueryFileTree/images/linux.png +0 -0
  55. assets/js/jqueryFileTree/images/music.png +0 -0
  56. assets/js/jqueryFileTree/images/pdf.png +0 -0
  57. assets/js/jqueryFileTree/images/php.png +0 -0
  58. assets/js/jqueryFileTree/images/picture.png +0 -0
  59. assets/js/jqueryFileTree/images/ppt.png +0 -0
  60. assets/js/jqueryFileTree/images/psd.png +0 -0
  61. assets/js/jqueryFileTree/images/ruby.png +0 -0
  62. assets/js/jqueryFileTree/images/script.png +0 -0
  63. assets/js/jqueryFileTree/images/spinner.gif +0 -0
  64. assets/js/jqueryFileTree/images/txt.png +0 -0
  65. assets/js/jqueryFileTree/images/xls.png +0 -0
  66. assets/js/jqueryFileTree/images/zip.png +0 -0
  67. assets/js/jqueryFileTree/jqueryFileTree.css +101 -0
  68. assets/js/jqueryFileTree/jqueryFileTree.js +95 -0
  69. download-monitor.php +535 -0
  70. includes/admin/class-dlm-admin-cpt.php +305 -0
  71. includes/admin/class-dlm-admin-dashboard.php +91 -0
  72. includes/admin/class-dlm-admin-insert.php +350 -0
  73. includes/admin/class-dlm-admin-media-browser.php +116 -0
  74. includes/admin/class-dlm-admin-writepanels.php +480 -0
  75. includes/admin/class-dlm-admin.php +413 -0
  76. includes/admin/class-dlm-category-walker.php +48 -0
  77. includes/admin/class-dlm-logging-list-table.php +171 -0
  78. includes/admin/html-downloadable-file-version.php +41 -0
  79. includes/admin/uaparser/CHANGELOG +169 -0
  80. includes/admin/uaparser/LICENSE +11 -0
  81. includes/admin/uaparser/README.md +125 -0
  82. includes/admin/uaparser/lib/json/JSON.php +867 -0
  83. includes/admin/uaparser/lib/json/jsonwrapper.php +21 -0
  84. includes/admin/uaparser/lib/spyc-0.5/COPYING +21 -0
  85. includes/admin/uaparser/lib/spyc-0.5/README +159 -0
  86. includes/admin/uaparser/lib/spyc-0.5/examples/yaml-dump.php +25 -0
  87. includes/admin/uaparser/lib/spyc-0.5/examples/yaml-load.php +21 -0
  88. includes/admin/uaparser/lib/spyc-0.5/spyc.php +1046 -0
  89. includes/admin/uaparser/lib/spyc-0.5/spyc.yaml +203 -0
  90. includes/admin/uaparser/lib/spyc-0.5/tests/DumpTest.php +130 -0
  91. includes/admin/uaparser/lib/spyc-0.5/tests/IndentTest.php +65 -0
  92. includes/admin/uaparser/lib/spyc-0.5/tests/ParseTest.php +322 -0
  93. includes/admin/uaparser/lib/spyc-0.5/tests/RoundTripTest.php +61 -0
  94. includes/admin/uaparser/lib/spyc-0.5/tests/failing1.yaml +2 -0
  95. includes/admin/uaparser/lib/spyc-0.5/tests/indent_1.yaml +59 -0
  96. includes/admin/uaparser/lib/spyc-0.5/tests/quotes.yaml +8 -0
  97. includes/admin/uaparser/log/.gitignore +2 -0
  98. includes/admin/uaparser/log/README.md +1 -0
  99. includes/admin/uaparser/resources/regexes.json +1193 -0
  100. includes/admin/uaparser/uaparser-cli.php +279 -0
  101. includes/admin/uaparser/uaparser-test.php +104 -0
  102. includes/admin/uaparser/uaparser.php +303 -0
  103. includes/class-dlm-ajax-handler.php +137 -0
  104. includes/class-dlm-download-handler.php +331 -0
  105. includes/class-dlm-download-version.php +88 -0
  106. includes/class-dlm-download.php +458 -0
  107. includes/class-dlm-logging.php +87 -0
  108. includes/class-dlm-shortcodes.php +324 -0
  109. includes/widgets/class-dlm-widget-downloads.php +196 -0
  110. readme.txt +117 -0
  111. templates/content-download-box.php +28 -0
  112. templates/content-download-button.php +11 -0
  113. templates/content-download-filename.php +10 -0
  114. templates/content-download.php +10 -0
assets/css/admin.css ADDED
@@ -0,0 +1,588 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Menu */
2
+ #adminmenu #menu-posts-dlm_download div.wp-menu-image {
3
+ background: transparent url(../images/menu_icon.png) no-repeat 1px -28px;
4
+ height: 23px;
5
+ }
6
+ #adminmenu #menu-posts-dlm_download.wp-menu-open div.wp-menu-image,
7
+ #adminmenu #menu-posts-dlm_download:hover div.wp-menu-image {
8
+ background-position: 1px 0;
9
+ }
10
+ #adminmenu #menu-posts-dlm_download img {
11
+ display: none;
12
+ }
13
+ /* Widgets */
14
+ table.download_chart {
15
+ width: 100%;
16
+ }
17
+ table.download_chart thead {
18
+ display: none;
19
+ }
20
+ table.download_chart td,
21
+ table.download_chart th {
22
+ padding: 5px;
23
+ vertical-align: middle;
24
+ line-height: 1.5em;
25
+ }
26
+ table.download_chart th {
27
+ text-align: left;
28
+ font-weight: normal;
29
+ padding-left: 0;
30
+ }
31
+ table.download_chart td:last-child {
32
+ padding-right: 0;
33
+ }
34
+ table.download_chart tr:first-child td,
35
+ table.download_chart tr:first-child th {
36
+ padding-top: 0;
37
+ }
38
+ table.download_chart tr:last-child td,
39
+ table.download_chart tr:last-child th {
40
+ border-bottom: 0;
41
+ padding-bottom: 0;
42
+ }
43
+ table.download_chart span.bar {
44
+ padding: 0 0 0 1px;
45
+ height: 1.5em;
46
+ float: left;
47
+ margin-right: 5px;
48
+ -moz-box-sizing: border-box;
49
+ -webkit-box-sizing: border-box;
50
+ box-sizing: border-box;
51
+ -moz-border-radius: 2px;
52
+ -webkit-border-radius: 2px;
53
+ border-radius: 2px;
54
+ bprder: 1px solid ##6e6e6e;
55
+ background: #6e6e6e;
56
+ background-image: -webkit-gradient(linear, left bottom, left top, from(#747474), to(#6e6e6e));
57
+ background-image: -webkit-linear-gradient(bottom, #747474, #6e6e6e);
58
+ background-image: -moz-linear-gradient(bottom, #747474, #6e6e6e);
59
+ background-image: -o-linear-gradient(bottom, #747474, #6e6e6e);
60
+ background-image: linear-gradient(to top, #747474, #6e6e6e);
61
+ }
62
+ /* Logs */
63
+ #dlm_logs .tablenav.top {
64
+ display: none;
65
+ }
66
+ #dlm_logs td {
67
+ padding: 7px 7px;
68
+ vertical-align: middle;
69
+ }
70
+ #dlm_logs .column-status {
71
+ width: 4em;
72
+ }
73
+ #dlm_logs td.column-status {
74
+ font-size: 10px;
75
+ vertical-align: middle;
76
+ text-align: center;
77
+ }
78
+ #dlm_logs td.column-status span {
79
+ color: #fff;
80
+ font-weight: normal;
81
+ -moz-border-radius: 50%;
82
+ -webkit-border-radius: 50%;
83
+ border-radius: 50%;
84
+ height: 16px;
85
+ line-height: 16px;
86
+ width: 16px;
87
+ display: inline-block;
88
+ }
89
+ #dlm_logs td.column-status .failed {
90
+ background-color: #df9d1d;
91
+ }
92
+ #dlm_logs td.column-status .redirected {
93
+ background-color: #74a721;
94
+ }
95
+ #dlm_logs td.column-status .completed {
96
+ background-color: #1c769b;
97
+ }
98
+ #dlm_logs td.column-user_ip,
99
+ #dlm_logs td.column-user_agent {
100
+ font-family: monospace;
101
+ font-weight: normal;
102
+ }
103
+ #dlm_logs span.description {
104
+ font-weight: normal;
105
+ }
106
+ #dlm_logs .column-download,
107
+ #dlm_logs .column-file,
108
+ #dlm_logs .column-user,
109
+ #dlm_logs .column-user_ua {
110
+ width: 16% !important;
111
+ }
112
+ #dlm_logs .column-user_ip,
113
+ #dlm_logs .column-date {
114
+ width: 6% !important;
115
+ }
116
+ /* Admin icon */
117
+ .icon32-posts-dlm_download {
118
+ background: url(../images/downloads-icon.gif) no-repeat scroll -7px -5px !important;
119
+ }
120
+ /* File lists */
121
+ ul.download_monitor_file_browser {
122
+ list-style: none outside;
123
+ margin: 0;
124
+ border: 1px solid #ddd;
125
+ -moz-border-radius: 3px;
126
+ -webkit-border-radius: 3px;
127
+ border-radius: 3px;
128
+ background: #f9f9f9;
129
+ padding: 5px 5px;
130
+ -moz-box-shadow: inset 0 0 0 1px #fff;
131
+ -webkit-box-shadow: inset 0 0 0 1px #fff;
132
+ box-shadow: inset 0 0 0 1px #fff;
133
+ }
134
+ ul.download_monitor_file_browser li {
135
+ padding: 0;
136
+ margin: 0;
137
+ }
138
+ ul.download_monitor_file_browser li a {
139
+ color: #21759B;
140
+ display: block;
141
+ padding: 4px 0 4px 28px;
142
+ background: #ffffff url(../images/filetype_icons/document.png) no-repeat 4px center;
143
+ text-decoration: none;
144
+ }
145
+ ul.download_monitor_file_browser li a:hover {
146
+ background-color: #eaf2fa;
147
+ }
148
+ ul.download_monitor_file_browser li a.folder {
149
+ background-image: url(../images/folder-horizontal.png);
150
+ }
151
+ ul.download_monitor_file_browser li a.folder_open {
152
+ background-image: url(../images/folder-horizontal-open.png);
153
+ }
154
+ ul.download_monitor_file_browser li a.filetype-html,
155
+ ul.download_monitor_file_browser li a.filetype-js,
156
+ ul.download_monitor_file_browser li a.filetype-htm,
157
+ ul.download_monitor_file_browser li a.filetype-php,
158
+ ul.download_monitor_file_browser li a.filetype-asp,
159
+ ul.download_monitor_file_browser li a.filetype-css {
160
+ background-image: url(../images/filetype_icons/document-code.png);
161
+ }
162
+ ul.download_monitor_file_browser li a.filetype-xlsx,
163
+ ul.download_monitor_file_browser li a.filetype-xlsm,
164
+ ul.download_monitor_file_browser li a.filetype-xlsb,
165
+ ul.download_monitor_file_browser li a.filetype-xltx,
166
+ ul.download_monitor_file_browser li a.filetype-xltm,
167
+ ul.download_monitor_file_browser li a.filetype-xls,
168
+ ul.download_monitor_file_browser li a.filetype-xlt {
169
+ background-image: url(../images/filetype_icons/document-excel.png);
170
+ }
171
+ ul.download_monitor_file_browser li a.filetype-mov,
172
+ ul.download_monitor_file_browser li a.filetype-avi,
173
+ ul.download_monitor_file_browser li a.filetype-3g2,
174
+ ul.download_monitor_file_browser li a.filetype-3gp,
175
+ ul.download_monitor_file_browser li a.filetype-asf,
176
+ ul.download_monitor_file_browser li a.filetype-asx,
177
+ ul.download_monitor_file_browser li a.filetype-mp4,
178
+ ul.download_monitor_file_browser li a.filetype-mpg,
179
+ ul.download_monitor_file_browser li a.filetype-rm,
180
+ ul.download_monitor_file_browser li a.filetype-srt,
181
+ ul.download_monitor_file_browser li a.filetype-vob,
182
+ ul.download_monitor_file_browser li a.filetype-wmv {
183
+ background-image: url(../images/filetype_icons/document-film.png);
184
+ }
185
+ ul.download_monitor_file_browser li a.filetype-flv,
186
+ ul.download_monitor_file_browser li a.filetype-swf {
187
+ background-image: url(../images/filetype_icons/document-flash-movie.png);
188
+ }
189
+ ul.download_monitor_file_browser li a.filetype-ai,
190
+ ul.download_monitor_file_browser li a.filetype-eps,
191
+ ul.download_monitor_file_browser li a.filetype-ps,
192
+ ul.download_monitor_file_browser li a.filetype-svg {
193
+ background-image: url(../images/filetype_icons/document-illustrator.png);
194
+ }
195
+ ul.download_monitor_file_browser li a.filetype-bmp,
196
+ ul.download_monitor_file_browser li a.filetype-dds,
197
+ ul.download_monitor_file_browser li a.filetype-gif,
198
+ ul.download_monitor_file_browser li a.filetype-jpg,
199
+ ul.download_monitor_file_browser li a.filetype-jpeg,
200
+ ul.download_monitor_file_browser li a.filetype-png,
201
+ ul.download_monitor_file_browser li a.filetype-pspimage,
202
+ ul.download_monitor_file_browser li a.filetype-tga,
203
+ ul.download_monitor_file_browser li a.filetype-thm,
204
+ ul.download_monitor_file_browser li a.filetype-tif,
205
+ ul.download_monitor_file_browser li a.filetype-yuv,
206
+ ul.download_monitor_file_browser li a.filetype-ico {
207
+ background-image: url(../images/filetype_icons/document-image.png);
208
+ }
209
+ ul.download_monitor_file_browser li a.filetype-mp3,
210
+ ul.download_monitor_file_browser li a.filetype-aif,
211
+ ul.download_monitor_file_browser li a.filetype-iff,
212
+ ul.download_monitor_file_browser li a.filetype-m3u,
213
+ ul.download_monitor_file_browser li a.filetype-m4a,
214
+ ul.download_monitor_file_browser li a.filetype-mid,
215
+ ul.download_monitor_file_browser li a.filetype-midi,
216
+ ul.download_monitor_file_browser li a.filetype-mpa,
217
+ ul.download_monitor_file_browser li a.filetype-ra,
218
+ ul.download_monitor_file_browser li a.filetype-wav,
219
+ ul.download_monitor_file_browser li a.filetype-wma {
220
+ background-image: url(../images/filetype_icons/document-music.png);
221
+ }
222
+ ul.download_monitor_file_browser li a.filetype-pdf {
223
+ background-image: url(../images/filetype_icons/document-pdf.png);
224
+ }
225
+ ul.download_monitor_file_browser li a.filetype-psd {
226
+ background-image: url(../images/filetype_icons/document-photoshop.png);
227
+ }
228
+ ul.download_monitor_file_browser li a.filetype-pps,
229
+ ul.download_monitor_file_browser li a.filetype-ppt,
230
+ ul.download_monitor_file_browser li a.filetype-pptx {
231
+ background-image: url(../images/filetype_icons/document-powerpoint.png);
232
+ }
233
+ ul.download_monitor_file_browser li a.filetype-rtf,
234
+ ul.download_monitor_file_browser li a.filetype-wpd,
235
+ ul.download_monitor_file_browser li a.filetype-wps,
236
+ ul.download_monitor_file_browser li a.filetype-odt,
237
+ ul.download_monitor_file_browser li a.filetype-pages {
238
+ background-image: url(../images/filetype_icons/document-text-image.png);
239
+ }
240
+ ul.download_monitor_file_browser li a.filetype-txt,
241
+ ul.download_monitor_file_browser li a.filetype-tex,
242
+ ul.download_monitor_file_browser li a.filetype-wps,
243
+ ul.download_monitor_file_browser li a.filetype-odt,
244
+ ul.download_monitor_file_browser li a.filetype-log,
245
+ ul.download_monitor_file_browser li a.filetype-csv,
246
+ ul.download_monitor_file_browser li a.filetype-xml {
247
+ background-image: url(../images/filetype_icons/document-text.png);
248
+ }
249
+ ul.download_monitor_file_browser li a.filetype-doc,
250
+ ul.download_monitor_file_browser li a.filetype-docx {
251
+ background-image: url(../images/filetype_icons/document-word-text.png);
252
+ }
253
+ ul.download_monitor_file_browser li a.filetype-7z,
254
+ ul.download_monitor_file_browser li a.filetype-zip,
255
+ ul.download_monitor_file_browser li a.filetype-rar,
256
+ ul.download_monitor_file_browser li a.filetype-zipx,
257
+ ul.download_monitor_file_browser li a.filetype-sit,
258
+ ul.download_monitor_file_browser li a.filetype-sitx,
259
+ ul.download_monitor_file_browser li a.filetype-gz,
260
+ ul.download_monitor_file_browser li a.filetype-tax-gz {
261
+ background-image: url(../images/filetype_icons/document-zipper.png);
262
+ }
263
+ ul.download_monitor_file_browser li.nofiles {
264
+ color: #999;
265
+ font-style: italic;
266
+ padding: 4px 0;
267
+ }
268
+ ul.download_monitor_file_browser li ul {
269
+ list-style: none outside;
270
+ margin: 0;
271
+ padding: 0 0 0 28px;
272
+ }
273
+ ul.download_monitor_file_browser li ul.loading {
274
+ background: url(../images/ajax-loader.gif) no-repeat 24px 0;
275
+ height: 32px;
276
+ }
277
+ /* Download list */
278
+ table.wp-list-table .column-thumb {
279
+ width: 52px;
280
+ text-align: center;
281
+ white-space: nowrap;
282
+ }
283
+ table.wp-list-table .column-dlm_download_cat,
284
+ table.wp-list-table .column-dlm_download_tag,
285
+ table.wp-list-table .column-file {
286
+ width: 11% !important;
287
+ }
288
+ table.wp-list-table .column-id,
289
+ table.wp-list-table .column-version {
290
+ width: 6%;
291
+ }
292
+ table.wp-list-table td.column-members_only,
293
+ table.wp-list-table td.column-featured {
294
+ width: 46px;
295
+ text-align: left;
296
+ padding-left: 11px;
297
+ }
298
+ table.wp-list-table td.column-download_count {
299
+ width: 6%;
300
+ text-align: left;
301
+ padding-left: 11px;
302
+ }
303
+ table.wp-list-table th.column-members_only,
304
+ table.wp-list-table th.column-featured {
305
+ width: 46px;
306
+ }
307
+ table.wp-list-table th.column-members_only img,
308
+ table.wp-list-table th.column-featured img {
309
+ padding-top: 3px !important;
310
+ }
311
+ table.wp-list-table th.column-download_count {
312
+ width: 6%;
313
+ }
314
+ table.wp-list-table th.column-download_count img {
315
+ padding-top: 3px !important;
316
+ }
317
+ table.wp-list-table img {
318
+ margin: 1px 2px;
319
+ }
320
+ table.wp-list-table .column-thumb img {
321
+ padding: 2px;
322
+ margin: 0;
323
+ border: 1px solid #dfdfdf;
324
+ vertical-align: middle;
325
+ width: 32px;
326
+ height: 32px;
327
+ }
328
+ table.wp-list-table span.na {
329
+ color: #999;
330
+ }
331
+ /* Meta boxes */
332
+ #download-monitor-file .inside {
333
+ padding: 0;
334
+ margin: 0;
335
+ }
336
+ #download-monitor-file .dlm-metaboxes-wrapper .expand_all,
337
+ #download-monitor-file .dlm-metaboxes-wrapper .close_all {
338
+ float: right;
339
+ margin-left: 9px;
340
+ line-height: 22px;
341
+ }
342
+ #download-monitor-file .dlm-metaboxes-wrapper .expand_all {
343
+ padding-left: 14px;
344
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAI0lEQVQYV2PcvXv3fwY8gJEoBa6urozYDAFpBpswGBTg8yYA3kYtiYVhDhIAAAAASUVORK5CYII=) no-repeat left;
345
+ }
346
+ #download-monitor-file .dlm-metaboxes-wrapper .close_all {
347
+ padding-left: 14px;
348
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAHUlEQVQYV2NkIAAYd+/e/R+fGsIKCFpBUAHt3QAASf0MyQbFJwoAAAAASUVORK5CYII=) no-repeat left;
349
+ }
350
+ #download-monitor-file .dlm-metaboxes-wrapper p.toolbar {
351
+ margin: 0 !important;
352
+ border-top: 1px solid white;
353
+ border-bottom: 1px solid #DFDFDF;
354
+ padding: 7px 12px !important;
355
+ overflow: hidden;
356
+ zoom: 1;
357
+ }
358
+ #download-monitor-file .dlm-metaboxes-wrapper p.toolbar a.button {
359
+ float: left;
360
+ margin: 0;
361
+ }
362
+ #download-monitor-file .dlm-metaboxes-wrapper select.attribute_taxonomy,
363
+ #download-monitor-file .dlm-metaboxes-wrapper button.add_attribute,
364
+ #download-monitor-file .dlm-metaboxes-wrapper button.add_variable_attribute,
365
+ #download-monitor-file .dlm-metaboxes-wrapper .fr {
366
+ float: right;
367
+ margin: 0 0 0 6px;
368
+ }
369
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metaboxes {
370
+ padding: 0 12px 0;
371
+ }
372
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox-sortable-placeholder {
373
+ border-color: #bbb;
374
+ background-color: #f5f5f5;
375
+ margin: 9px 0;
376
+ border-width: 1px;
377
+ border-style: dashed;
378
+ }
379
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox {
380
+ background: #ececec;
381
+ border: 1px solid #ececec;
382
+ margin: 9px 0 !important;
383
+ }
384
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox h3 {
385
+ margin: 0 !important;
386
+ padding: 6px !important;
387
+ font-size: 1em !important;
388
+ overflow: hidden;
389
+ zoom: 1;
390
+ cursor: move;
391
+ }
392
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox h3 button {
393
+ float: right;
394
+ }
395
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox h3 strong {
396
+ font-weight: normal;
397
+ line-height: 24px;
398
+ }
399
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox h3 select {
400
+ font-family: sans-serif;
401
+ }
402
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox h3 .handlediv {
403
+ background-position: 6px 5px !important;
404
+ display: none;
405
+ height: 24px;
406
+ }
407
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox:hover h3 .handlediv {
408
+ display: block;
409
+ }
410
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table {
411
+ width: 100%;
412
+ position: relative;
413
+ background: #fff;
414
+ padding: 3px;
415
+ }
416
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td {
417
+ text-align: left;
418
+ padding: 6px 6px;
419
+ vertical-align: top;
420
+ border: 0;
421
+ line-height: 26px;
422
+ }
423
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td label {
424
+ text-align: left;
425
+ display: block;
426
+ line-height: 21px;
427
+ }
428
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input {
429
+ float: left;
430
+ min-width: 200px;
431
+ }
432
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input,
433
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td textarea {
434
+ width: 100%;
435
+ margin: 0;
436
+ display: block;
437
+ font-size: 14px;
438
+ padding: 4px;
439
+ color: #555;
440
+ }
441
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td textarea {
442
+ height: 7.5em;
443
+ }
444
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td select {
445
+ width: 100%;
446
+ }
447
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input.short {
448
+ width: 200px;
449
+ }
450
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input.checkbox {
451
+ width: auto;
452
+ min-width: inherit;
453
+ vertical-align: middle;
454
+ display: inline;
455
+ float: none;
456
+ }
457
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input.date-picker-field {
458
+ width: 50%;
459
+ min-width: inherit;
460
+ float: none;
461
+ display: inline;
462
+ }
463
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input.minute,
464
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td input.hour {
465
+ width: 2em;
466
+ min-width: inherit;
467
+ float: none;
468
+ display: inline;
469
+ }
470
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table td.attribute_name {
471
+ width: 200px;
472
+ }
473
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table .plus,
474
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table .minus {
475
+ margin-top: 6px;
476
+ }
477
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table .fl {
478
+ float: left;
479
+ }
480
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox table .fr {
481
+ float: right;
482
+ }
483
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox:first-child {
484
+ /* main file */
485
+
486
+ }
487
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm-metabox:first-child h3 {
488
+ background: #EAF2FA;
489
+ background-image: linear-gradient(bottom, #dae6f2 0%, #eaf2fa 100%);
490
+ background-image: -o-linear-gradient(bottom, #dae6f2 0%, #eaf2fa 100%);
491
+ background-image: -moz-linear-gradient(bottom, #dae6f2 0%, #eaf2fa 100%);
492
+ background-image: -webkit-linear-gradient(bottom, #dae6f2 0%, #eaf2fa 100%);
493
+ background-image: -ms-linear-gradient(bottom, #dae6f2 0%, #eaf2fa 100%);
494
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #dae6f2), color-stop(1, #eaf2fa));
495
+ border-color: #d3deea;
496
+ color: #21759B;
497
+ }
498
+ #download-monitor-file .dlm-metaboxes-wrapper .plus {
499
+ padding-left: 20px;
500
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARklEQVQYGWP8//8/AzGACV3Rnj17/oMwujiGQnQFMD7RChlBbsRmFcwkEO3i4sJImonIumGmg0xBFifaRKIVgj2DbAUuNgCfThpracSKqwAAAABJRU5ErkJggg==) no-repeat 7px 5px;
501
+ }
502
+ #download-monitor-file .dlm-metaboxes-wrapper .minus {
503
+ padding-left: 20px;
504
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAKUlEQVQYlWP8//8/Az7AhFeWGAUsDAwMDHv27MFqj4uLCyNBExhp70gAohwKC5KJ+HQAAAAASUVORK5CYII=) no-repeat 7px 6px;
505
+ }
506
+ #download-monitor-file .dlm-metaboxes-wrapper .dlm_upload_file {
507
+ padding-left: 20px;
508
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAMklEQVQYV2NkQAK7d+/+D+K6uroywoThDJgkTAKmCKwAXRJZESMuSQwrkE3C6gbaKQAAYS4fOXZlROQAAAAASUVORK5CYII=) no-repeat 7px 5px;
509
+ }
510
+ #download-monitor-file .dlm-metaboxes-wrapper .browse_for_file {
511
+ padding-left: 20px;
512
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAK0lEQVQYV2NkIAAYd+/e/R+mxtXVlRFdPYoCbIaBFWDTCVIMkqOXAnw+BQCbpycpLX4gIQAAAABJRU5ErkJggg==) no-repeat 7px 5px;
513
+ }
514
+ /* Writepanel forms */
515
+ #download-monitor-options .inside {
516
+ padding: 0;
517
+ margin: 0;
518
+ }
519
+ #download-monitor-options .form-field-checkbox label {
520
+ display: inline;
521
+ padding: 0 0 4px;
522
+ }
523
+ #download-monitor-options .form-field-checkbox input {
524
+ display: inline;
525
+ width: auto;
526
+ vertical-align: middle;
527
+ }
528
+ #download-monitor-options .form-field-checkbox .description {
529
+ padding: 4px 0 0 0;
530
+ display: block;
531
+ }
532
+ #download-monitor-options .access_permissions {
533
+ margin: 0 !important;
534
+ border-top: 1px solid white;
535
+ border-bottom: 1px solid #DFDFDF;
536
+ padding: 7px 12px !important;
537
+ overflow: hidden;
538
+ zoom: 1;
539
+ }
540
+ #download-monitor-options .access_permissions h4 {
541
+ margin-bottom: .5em;
542
+ margin: 0 0 .5em;
543
+ }
544
+ #download-monitor-options .access_permissions ul {
545
+ background: #fff;
546
+ border: 1px solid #dfdfdf;
547
+ height: 110px;
548
+ overflow: auto;
549
+ padding: .5em .9em;
550
+ margin: 0;
551
+ }
552
+ #download-monitor-options p {
553
+ margin: 0 !important;
554
+ border-top: 1px solid white;
555
+ border-bottom: 1px solid #DFDFDF;
556
+ padding: 7px 12px !important;
557
+ overflow: hidden;
558
+ zoom: 1;
559
+ }
560
+ #insert-download {
561
+ padding: 20px 20px;
562
+ }
563
+ #insert-download .updated,
564
+ #insert-download .error {
565
+ margin-left: 0;
566
+ margin-right: 0;
567
+ }
568
+ #insert-download h2 {
569
+ margin-top: 0;
570
+ }
571
+ #insert-download label {
572
+ margin: 0 0 .25em 0;
573
+ display: block;
574
+ font-size: 1.1em;
575
+ }
576
+ #insert-download span.description {
577
+ display: block;
578
+ }
579
+ #insert-download select.input,
580
+ #insert-download input.input {
581
+ margin: 0 0 .25em 0;
582
+ display: block;
583
+ width: 100%;
584
+ font-size: 1.1em;
585
+ }
586
+ #insert-download .add_link {
587
+ float: right;
588
+ }
assets/css/admin.less ADDED
@@ -0,0 +1,523 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Menu */
2
+ #adminmenu {
3
+ #menu-posts-dlm_download {
4
+ div.wp-menu-image {
5
+ background: transparent url(../images/menu_icon.png) no-repeat 1px -28px;
6
+ height: 23px;
7
+ }
8
+ &.wp-menu-open div.wp-menu-image, &:hover div.wp-menu-image {
9
+ background-position: 1px 0;
10
+ }
11
+ img {
12
+ display: none;
13
+ }
14
+ }
15
+ }
16
+
17
+ /* Widgets */
18
+ table.download_chart {
19
+ width: 100%;
20
+ thead {
21
+ display: none;
22
+ }
23
+ td, th {
24
+ padding: 5px;
25
+ vertical-align: middle;
26
+ line-height: 1.5em;
27
+ }
28
+ th {
29
+ text-align: left;
30
+ font-weight: normal;
31
+ padding-left: 0;
32
+ }
33
+ td:last-child {
34
+ padding-right: 0;
35
+ }
36
+ tr:first-child {
37
+ td, th {
38
+ padding-top: 0;
39
+ }
40
+ }
41
+ tr:last-child {
42
+ td, th {
43
+ border-bottom: 0;
44
+ padding-bottom: 0;
45
+ }
46
+ }
47
+ span.bar {
48
+ padding: 0 0 0 1px;
49
+ height: 1.5em;
50
+ float: left;
51
+ margin-right: 5px;
52
+ -moz-box-sizing: border-box;
53
+ -webkit-box-sizing: border-box;
54
+ box-sizing: border-box;
55
+ -moz-border-radius: 2px;
56
+ -webkit-border-radius: 2px;
57
+ border-radius: 2px;
58
+ bprder:1px solid ##6e6e6e;
59
+ background:#6e6e6e;
60
+ background-image:-webkit-gradient(linear,left bottom,left top,from(#747474),to(#6e6e6e));
61
+ background-image:-webkit-linear-gradient(bottom,#747474,#6e6e6e);
62
+ background-image:-moz-linear-gradient(bottom,#747474,#6e6e6e);
63
+ background-image:-o-linear-gradient(bottom,#747474,#6e6e6e);
64
+ background-image:linear-gradient(to top,#747474,#6e6e6e);
65
+ }
66
+ }
67
+
68
+ /* Logs */
69
+ #dlm_logs {
70
+ .tablenav.top {
71
+ display: none;
72
+ }
73
+ td {
74
+ padding: 7px 7px;
75
+ vertical-align: middle;
76
+ }
77
+ .column-status {
78
+ width: 4em;
79
+ }
80
+ td.column-status {
81
+ font-size: 10px;
82
+ vertical-align: middle;
83
+ text-align: center;
84
+ span {
85
+ color: #fff;
86
+ font-weight: normal;
87
+ -moz-border-radius: 50%;
88
+ -webkit-border-radius: 50%;
89
+ border-radius: 50%;
90
+ height: 16px;
91
+ line-height: 16px;
92
+ width: 16px;
93
+ display: inline-block;
94
+ }
95
+ .failed {
96
+ background-color: #df9d1d;
97
+ }
98
+ .redirected {
99
+ background-color: #74a721;
100
+ }
101
+ .completed {
102
+ background-color: #1c769b;
103
+ }
104
+ }
105
+ td.column-user_ip, td.column-user_agent {
106
+ font-family: monospace;
107
+ font-weight: normal;
108
+ }
109
+ span.description {
110
+ font-weight: normal;
111
+ }
112
+ .column-download, .column-file, .column-user, .column-user_ua {
113
+ width: 16% !important;
114
+ }
115
+ .column-user_ip, .column-date {
116
+ width: 6% !important;
117
+ }
118
+ }
119
+
120
+ /* Admin icon */
121
+ .icon32-posts-dlm_download {
122
+ background: url(../images/downloads-icon.gif) no-repeat scroll -7px -5px !important;
123
+ }
124
+
125
+ /* File lists */
126
+ ul.download_monitor_file_browser {
127
+ list-style: none outside;
128
+ margin: 0;
129
+ border: 1px solid #ddd;
130
+ -moz-border-radius: 3px;
131
+ -webkit-border-radius: 3px;
132
+ border-radius: 3px;
133
+ background: #f9f9f9;
134
+ padding: 5px 5px;
135
+ -moz-box-shadow: inset 0 0 0 1px #fff;
136
+ -webkit-box-shadow: inset 0 0 0 1px #fff;
137
+ box-shadow: inset 0 0 0 1px #fff;
138
+
139
+ li {
140
+ padding: 0;
141
+ margin: 0;
142
+ a {
143
+ color: #21759B;
144
+ display: block;
145
+ padding: 4px 0 4px 28px;
146
+ background: #fff url(../images/filetype_icons/document.png) no-repeat 4px center;
147
+ text-decoration: none;
148
+
149
+ &:hover {
150
+ background-color: #eaf2fa;
151
+ }
152
+
153
+ &.folder { background-image: url(../images/folder-horizontal.png); }
154
+
155
+ &.folder_open { background-image: url(../images/folder-horizontal-open.png); }
156
+
157
+ &.filetype-html, &.filetype-js, &.filetype-htm, &.filetype-php, &.filetype-asp, &.filetype-css { background-image: url(../images/filetype_icons/document-code.png); }
158
+
159
+ &.filetype-xlsx, &.filetype-xlsm, &.filetype-xlsb, &.filetype-xltx, &.filetype-xltm, &.filetype-xls, &.filetype-xlt { background-image: url(../images/filetype_icons/document-excel.png); }
160
+
161
+ &.filetype-mov, &.filetype-avi, &.filetype-3g2, &.filetype-3gp, &.filetype-asf, &.filetype-asx, &.filetype-mp4, &.filetype-mpg, &.filetype-rm, &.filetype-srt, &.filetype-vob, &.filetype-wmv { background-image: url(../images/filetype_icons/document-film.png); }
162
+
163
+ &.filetype-flv, &.filetype-swf { background-image: url(../images/filetype_icons/document-flash-movie.png); }
164
+
165
+ &.filetype-ai, &.filetype-eps, &.filetype-ps, &.filetype-svg { background-image: url(../images/filetype_icons/document-illustrator.png); }
166
+
167
+ &.filetype-bmp, &.filetype-dds, &.filetype-gif, &.filetype-jpg, &.filetype-jpeg, &.filetype-png, &.filetype-pspimage, &.filetype-tga, &.filetype-thm, &.filetype-tif, &.filetype-yuv, &.filetype-ico { background-image: url(../images/filetype_icons/document-image.png); }
168
+
169
+ &.filetype-mp3, &.filetype-aif, &.filetype-iff, &.filetype-m3u, &.filetype-m4a, &.filetype-mid, &.filetype-midi, &.filetype-mpa, &.filetype-ra, &.filetype-wav, &.filetype-wma { background-image: url(../images/filetype_icons/document-music.png); }
170
+
171
+ &.filetype-pdf { background-image: url(../images/filetype_icons/document-pdf.png); }
172
+
173
+ &.filetype-psd { background-image: url(../images/filetype_icons/document-photoshop.png); }
174
+
175
+ &.filetype-pps, &.filetype-ppt, &.filetype-pptx { background-image: url(../images/filetype_icons/document-powerpoint.png); }
176
+
177
+ &.filetype-rtf, &.filetype-wpd, &.filetype-wps, &.filetype-odt, &.filetype-pages { background-image: url(../images/filetype_icons/document-text-image.png); }
178
+
179
+ &.filetype-txt, &.filetype-tex, &.filetype-wps, &.filetype-odt, &.filetype-log, &.filetype-csv, &.filetype-xml { background-image: url(../images/filetype_icons/document-text.png); }
180
+
181
+ &.filetype-doc, &.filetype-docx { background-image: url(../images/filetype_icons/document-word-text.png); }
182
+
183
+ &.filetype-7z, &.filetype-zip, &.filetype-rar, &.filetype-zipx, &.filetype-sit, &.filetype-sitx, &.filetype-gz, &.filetype-tax-gz { background-image: url(../images/filetype_icons/document-zipper.png); }
184
+ }
185
+
186
+ &.nofiles {
187
+ color: #999;
188
+ font-style: italic;
189
+ padding: 4px 0;
190
+ }
191
+
192
+ ul {
193
+ list-style: none outside;
194
+ margin: 0;
195
+ padding: 0 0 0 28px;
196
+
197
+ &.loading {
198
+ background: url(../images/ajax-loader.gif) no-repeat 24px 0;
199
+ height: 32px;
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ /* Download list */
206
+ table.wp-list-table {
207
+ .column-thumb {
208
+ width: 52px;
209
+ text-align: center;
210
+ white-space: nowrap
211
+ }
212
+ .column-dlm_download_cat, .column-dlm_download_tag, .column-file {
213
+ width: 11% !important;
214
+ }
215
+ .column-id, .column-version {
216
+ width: 6%;
217
+ }
218
+ td.column-members_only, td.column-featured {
219
+ width: 46px;
220
+ text-align: left;
221
+ padding-left: 11px;
222
+ }
223
+ td.column-download_count {
224
+ width: 6%;
225
+ text-align: left;
226
+ padding-left: 11px;
227
+ }
228
+ th.column-members_only, th.column-featured {
229
+ width: 46px;
230
+ img {
231
+ padding-top: 3px !important;
232
+ }
233
+ }
234
+ th.column-download_count {
235
+ width: 6%;
236
+ img {
237
+ padding-top: 3px !important;
238
+ }
239
+ }
240
+ img {
241
+ margin: 1px 2px;
242
+ }
243
+ .column-thumb img {
244
+ padding: 2px;
245
+ margin: 0;
246
+ border: 1px solid #dfdfdf;
247
+ vertical-align: middle;
248
+ width: 32px;
249
+ height: 32px;
250
+ }
251
+ span.na {
252
+ color: #999;
253
+ }
254
+ }
255
+
256
+ /* Meta boxes */
257
+ #download-monitor-file {
258
+ .inside {
259
+ padding: 0;
260
+ margin: 0;
261
+ }
262
+ .dlm-metaboxes-wrapper {
263
+ .expand_all, .close_all {
264
+ float: right;
265
+ margin-left: 9px;
266
+ line-height: 22px;
267
+ }
268
+ .expand_all {
269
+ padding-left: 14px;
270
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAI0lEQVQYV2PcvXv3fwY8gJEoBa6urozYDAFpBpswGBTg8yYA3kYtiYVhDhIAAAAASUVORK5CYII=) no-repeat left;
271
+ }
272
+ .close_all {
273
+ padding-left: 14px;
274
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAHUlEQVQYV2NkIAAYd+/e/R+fGsIKCFpBUAHt3QAASf0MyQbFJwoAAAAASUVORK5CYII=) no-repeat left;
275
+ }
276
+ p.toolbar {
277
+ margin: 0 !important;
278
+ border-top: 1px solid white;
279
+ border-bottom: 1px solid #DFDFDF;
280
+ padding: 7px 12px !important;
281
+ overflow: hidden;
282
+ zoom: 1;
283
+
284
+ a.button {
285
+ float: left;
286
+ margin: 0;
287
+ }
288
+ }
289
+ select.attribute_taxonomy, button.add_attribute, button.add_variable_attribute, .fr {
290
+ float: right;
291
+ margin: 0 0 0 6px;
292
+ }
293
+ .dlm-metaboxes {
294
+ padding: 0 12px 0;
295
+ }
296
+ .dlm-metabox-sortable-placeholder {
297
+ border-color:#bbb;
298
+ background-color:#f5f5f5;
299
+ margin: 9px 0;
300
+ border-width:1px;
301
+ border-style:dashed;
302
+ }
303
+ .dlm-metabox {
304
+ background: #ececec;
305
+ border: 1px solid #ececec;
306
+ margin: 9px 0 !important;
307
+ h3 {
308
+ margin: 0 !important;
309
+ padding: 6px !important;
310
+ font-size: 1em !important;
311
+ overflow: hidden;
312
+ zoom: 1;
313
+ cursor: move;
314
+ button {
315
+ float: right;
316
+ }
317
+ strong {
318
+ font-weight: normal;
319
+ line-height: 24px;
320
+ }
321
+ select {
322
+ font-family: sans-serif;
323
+ }
324
+ .handlediv {
325
+ background-position: 6px 5px !important;
326
+ display: none;
327
+ height: 24px;
328
+ }
329
+ }
330
+ &:hover h3 .handlediv {
331
+ display: block;
332
+ }
333
+ table {
334
+ width: 100%;
335
+ position: relative;
336
+ background: #fff;
337
+ padding: 3px;
338
+ td {
339
+ text-align: left;
340
+ padding: 6px 6px;
341
+ vertical-align: top;
342
+ border: 0;
343
+ line-height: 26px;
344
+ label {
345
+ text-align: left;
346
+ display: block;
347
+ line-height: 21px;
348
+ }
349
+ input {
350
+ float: left;
351
+ min-width: 200px;
352
+ }
353
+ input, textarea {
354
+ width: 100%;
355
+ margin: 0;
356
+ display: block;
357
+ font-size: 14px;
358
+ padding: 4px;
359
+ color: #555;
360
+ }
361
+ textarea {
362
+ height: 7.5em;
363
+ }
364
+ select {
365
+ width: 100%;
366
+ }
367
+ input.short {
368
+ width: 200px;
369
+ }
370
+ input.checkbox {
371
+ width: auto;
372
+ min-width: inherit;
373
+ vertical-align: middle;
374
+ display: inline;
375
+ float: none;
376
+ }
377
+ input.date-picker-field {
378
+ width: 50%;
379
+ min-width: inherit;
380
+ float: none;
381
+ display: inline;
382
+ }
383
+ input.minute, input.hour {
384
+ width: 2em;
385
+ min-width: inherit;
386
+ float: none;
387
+ display: inline;
388
+ }
389
+ }
390
+ td.attribute_name {
391
+ width: 200px;
392
+ }
393
+ .plus, .minus {
394
+ margin-top: 6px;
395
+ }
396
+ .fl {
397
+ float: left;
398
+ }
399
+ .fr {
400
+ float: right;
401
+ }
402
+ }
403
+ &:first-child {
404
+ /* main file */
405
+ h3 {
406
+ background: #EAF2FA;
407
+ background-image: linear-gradient(bottom, rgb(218,230,242) 0%, rgb(234,242,250) 100%);
408
+ background-image: -o-linear-gradient(bottom, rgb(218,230,242) 0%, rgb(234,242,250) 100%);
409
+ background-image: -moz-linear-gradient(bottom, rgb(218,230,242) 0%, rgb(234,242,250) 100%);
410
+ background-image: -webkit-linear-gradient(bottom, rgb(218,230,242) 0%, rgb(234,242,250) 100%);
411
+ background-image: -ms-linear-gradient(bottom, rgb(218,230,242) 0%, rgb(234,242,250) 100%);
412
+
413
+ background-image: -webkit-gradient(
414
+ linear,
415
+ left bottom,
416
+ left top,
417
+ color-stop(0, rgb(218,230,242)),
418
+ color-stop(1, rgb(234,242,250))
419
+ );
420
+ border-color: #d3deea;
421
+ color: #21759B;
422
+ }
423
+ }
424
+ }
425
+ .plus {
426
+ padding-left: 20px;
427
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARklEQVQYGWP8//8/AzGACV3Rnj17/oMwujiGQnQFMD7RChlBbsRmFcwkEO3i4sJImonIumGmg0xBFifaRKIVgj2DbAUuNgCfThpracSKqwAAAABJRU5ErkJggg==) no-repeat 7px 5px;
428
+ }
429
+ .minus {
430
+ padding-left: 20px;
431
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAKUlEQVQYlWP8//8/Az7AhFeWGAUsDAwMDHv27MFqj4uLCyNBExhp70gAohwKC5KJ+HQAAAAASUVORK5CYII=) no-repeat 7px 6px;
432
+ }
433
+ .dlm_upload_file {
434
+ padding-left: 20px;
435
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAMklEQVQYV2NkQAK7d+/+D+K6uroywoThDJgkTAKmCKwAXRJZESMuSQwrkE3C6gbaKQAAYS4fOXZlROQAAAAASUVORK5CYII=) no-repeat 7px 5px;
436
+ }
437
+ .browse_for_file {
438
+ padding-left: 20px;
439
+ background: #fefefe url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAK0lEQVQYV2NkIAAYd+/e/R+mxtXVlRFdPYoCbIaBFWDTCVIMkqOXAnw+BQCbpycpLX4gIQAAAABJRU5ErkJggg==) no-repeat 7px 5px;
440
+ }
441
+ }
442
+ }
443
+
444
+ /* Writepanel forms */
445
+ #download-monitor-options {
446
+ .inside {
447
+ padding: 0;
448
+ margin: 0;
449
+ }
450
+ .form-field-checkbox {
451
+ label {
452
+ display: inline;
453
+ padding: 0 0 4px;
454
+ }
455
+ input {
456
+ display: inline;
457
+ width: auto;
458
+ vertical-align: middle;
459
+ }
460
+ .description {
461
+ padding: 4px 0 0 0;
462
+ display: block;
463
+ }
464
+ }
465
+ .access_permissions {
466
+ margin: 0 !important;
467
+ border-top: 1px solid white;
468
+ border-bottom: 1px solid #DFDFDF;
469
+ padding: 7px 12px !important;
470
+ overflow: hidden;
471
+ zoom: 1;
472
+
473
+ h4 {
474
+ margin-bottom: .5em;
475
+ margin: 0 0 .5em;
476
+ }
477
+ ul {
478
+ background: #fff;
479
+ border: 1px solid #dfdfdf;
480
+ height: 110px;
481
+ overflow: auto;
482
+ padding: .5em .9em;
483
+ margin: 0;
484
+ }
485
+ }
486
+ p {
487
+ margin: 0 !important;
488
+ border-top: 1px solid white;
489
+ border-bottom: 1px solid #DFDFDF;
490
+ padding: 7px 12px !important;
491
+ overflow: hidden;
492
+ zoom: 1;
493
+ }
494
+ }
495
+
496
+ #insert-download {
497
+ padding: 20px 20px;
498
+
499
+ .updated, .error {
500
+ margin-left: 0;
501
+ margin-right: 0;
502
+ }
503
+ h2 {
504
+ margin-top: 0;
505
+ }
506
+ label {
507
+ margin: 0 0 .25em 0;
508
+ display: block;
509
+ font-size: 1.1em;
510
+ }
511
+ span.description {
512
+ display: block;
513
+ }
514
+ select.input, input.input {
515
+ margin: 0 0 .25em 0;
516
+ display: block;
517
+ width: 100%;
518
+ font-size: 1.1em;
519
+ }
520
+ .add_link {
521
+ float: right;
522
+ }
523
+ }
assets/css/frontend.css ADDED
@@ -0,0 +1 @@
 
1
+ .download-box{width:250px;border:1px solid #ccc;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;text-align:center;position:relative;margin:1em auto;box-shadow:0 2px 4px rgba(0,0,0,0.1),inset 0 1px 0 rgba(255,255,255,0.4)}.download-box img.wp-post-image{margin:0;padding:0;display:block;width:100%;-moz-border-radius:0;-webkit-border-radius:0;-moz-border-top-left-radius:3px;-moz-border-top-right-radius:3px;-webkit-border-top-left-radius:3px;-webkit-border-top-right-radius:3px;border-radius:0;border-top-left-radius:3px;border-top-right-radius:3px;box-shadow:inset 0 1px 0 rgba(255,255,255,0.4)}.download-box .download-box-content{padding:0 1em 1em}.download-box .download-count{-moz-border-radius:1em;-webkit-border-radius:1em;border-radius:1em;color:#777;text-shadow:0 1px 0 rgba(255,255,255,0.5);background:#ddd;box-shadow:0 2px 4px rgba(0,0,0,0.1),inset 0 1px 0 rgba(255,255,255,0.4);position:absolute;top:0;right:0;padding:.5em;height:1em;width:auto;min-width:1em;font-size:1em;text-align:center;vertical-align:middle;line-height:1em;border:1px solid #bbb;margin:-0.5em -0.5em 0 0}.download-button{text-align:center;text-decoration:none;padding:.75em 1em;color:#fff;display:block;font-size:1.2em;line-height:1.5em;background-color:#09c;background-image:-webkit-linear-gradient(#009fd4,#09c,#0086b2);background-image:-moz-linear-gradient(#009fd4,#09c,#0086b2);-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;text-shadow:0 -1px 0 rgba(0,0,0,0.5);box-shadow:0 2px 4px rgba(0,0,0,0.3),inset 0 1px 0 rgba(255,255,255,0.4);border:1px solid #0086b2;cursor:pointer}.download-button:hover{color:#fff;background-color:#09c;background-image:-webkit-linear-gradient(#09c,#0086b2);background-image:-moz-linear-gradient(#09c,#0086b2)}.download-button small{font-size:.8em;opacity:.8;display:block}.filetype-icon{padding-left:19px;background-repeat:no-repeat;background-position:left;background-image:url(../images/filetypes/document.png)}.filetype-pdf{background-image:url(../images/filetypes/document-pdf.png)}.filetype-m4r,.filetype-au,.filetype-snd,.filetype-mid,.filetype-midi,.filetype-kar,.filetype-mpga,.filetype-mp2,.filetype-mp3,.filetype-aif,.filetype-aiff,.filetype-aifc,.filetype-m3u,.filetype-ram,.filetype-rm,.filetype-rpm,.filetype-ra,.filetype-wav,.filetype-wave{background-image:url(../images/filetypes/document-music.png)}.filetype-mpeg,.filetype-mpg,.filetype-mpe,.filetype-qt,.filetype-mov,.filetype-mxu,.filetype-avi,.filetype-movie,.filetype-mp4,.filetype-divx{background-image:url(../images/filetypes/document-film.png)}.filetype-zip,.filetype-gz,.filetype-rar,.filetype-sit,.filetype-tar,.filetype-7z{background-image:url(../images/filetypes/document-zipper.png)}.filetype-xls,.filetype-tsv,.filetype-csv{background-image:url(../images/filetypes/document-excel.png)}.filetype-doc{background-image:url(../images/filetypes/document-word-text.png)}.filetype-ai{background-image:url(../images/filetypes/document-illustrator.png)}.filetype-swf{background-image:url(../images/filetypes/document-flash-movie.png)}.filetype-eps,.filetype-ps,.filetype-bmp,.filetype-gif,.filetype-ief,.filetype-jpeg,.filetype-jpg,.filetype-jpe,.filetype-png,.filetype-tiff,.filetype-tif,.filetype-djv,.filetype-wbmp,.filetype-ras,.filetype-pnm,.filetype-pbm,.filetype-pgm,.filetype-ppm,.filetype-rgb,.filetype-xbm,.filetype-xpm,.filetype-xwd{background-image:url(../images/filetypes/document-image.png)}.filetype-psd{background-image:url(../images/filetypes/document-photoshop.png)}.filetype-ppt{background-image:url(../images/filetypes/document-powerpoint.png)}.filetype-js,.filetype-css,.filetype-as,.filetype-htm,.filetype-htaccess,.filetype-sql,.filetype-html,.filetype-php,.filetype-xml,.filetype-xsl{background-image:url(../images/filetypes/document-code.png)}.filetype-rtx,.filetype-rtf{background-image:url(../images/filetypes/document-text-image.png)}.filetype-txt{background-image:url(../images/filetypes/document-text.png)}
assets/css/frontend.less ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .download-box {
2
+ width: 250px;
3
+ border: 1px solid #ccc;
4
+ -moz-border-radius: 4px;
5
+ -webkit-border-radius: 4px;
6
+ border-radius: 4px;
7
+ text-align: center;
8
+ position: relative;
9
+ margin: 1em auto;
10
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1),inset 0 1px 0 rgba(255,255,255,0.4);
11
+
12
+ img.wp-post-image {
13
+ margin: 0;
14
+ padding: 0;
15
+ display: block;
16
+ width: 100%;
17
+ -moz-border-radius: 0;
18
+ -webkit-border-radius: 0;
19
+ -moz-border-top-left-radius: 3px;
20
+ -moz-border-top-right-radius: 3px;
21
+ -webkit-border-top-left-radius: 3px;
22
+ -webkit-border-top-right-radius: 3px;
23
+ border-radius: 0;
24
+ border-top-left-radius: 3px;
25
+ border-top-right-radius: 3px;
26
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.4);
27
+ }
28
+
29
+ .download-box-content {
30
+ padding: 0 1em 1em;
31
+ }
32
+
33
+ .download-count {
34
+ -moz-border-radius: 1em;
35
+ -webkit-border-radius: 1em;
36
+ border-radius: 1em;
37
+ color: #777;
38
+ text-shadow: 0 1px 0 rgba(255,255,255,0.5);
39
+ background: #ddd;
40
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.4);
41
+ position: absolute;
42
+ top: 0;
43
+ right: 0;
44
+ padding: .5em;
45
+ height: 1em;
46
+ width: auto;
47
+ min-width: 1em;
48
+ font-size: 1em;
49
+ text-align: center;
50
+ vertical-align: middle;
51
+ line-height: 1em;
52
+ border: 1px solid #bbb;
53
+ margin: -.5em -.5em 0 0;
54
+ }
55
+ }
56
+
57
+ .download-button {
58
+ text-align: center;
59
+ text-decoration: none;
60
+ padding: 0.75em 1em;
61
+ color: #fff;
62
+ display: block;
63
+ font-size: 1.2em;
64
+ line-height: 1.5em;
65
+ background-color: #0099cc;
66
+ background-image: -webkit-linear-gradient(#009fd4, #0099cc, #0086b2);
67
+ background-image: -moz-linear-gradient(#009fd4, #0099cc, #0086b2);
68
+ -moz-border-radius: 4px;
69
+ -webkit-border-radius: 4px;
70
+ border-radius: 4px;
71
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.5);
72
+ box-shadow: 0 2px 4px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.4);
73
+ border: 1px solid #0086b2;
74
+ cursor: pointer;
75
+
76
+ &:hover {
77
+ color: #fff;
78
+ background-color: #0099cc;
79
+ background-image: -webkit-linear-gradient(#0099cc, #0086b2);
80
+ background-image: -moz-linear-gradient(#0099cc, #0086b2);
81
+ }
82
+
83
+ small {
84
+ font-size: 0.8em;
85
+ opacity: 0.8;
86
+ display: block;
87
+ }
88
+ }
89
+
90
+ .filetype-icon {
91
+ padding-left: 19px;
92
+ background-repeat: no-repeat;
93
+ background-position: left;
94
+ background-image: url(../images/filetypes/document.png);
95
+ }
96
+ .filetype-pdf {
97
+ background-image: url(../images/filetypes/document-pdf.png);
98
+ }
99
+ .filetype-m4r, .filetype-au, .filetype-snd, .filetype-mid, .filetype-midi, .filetype-kar, .filetype-mpga, .filetype-mp2, .filetype-mp3, .filetype-aif, .filetype-aiff, .filetype-aifc, .filetype-m3u, .filetype-ram, .filetype-rm, .filetype-rpm, .filetype-ra, .filetype-wav, .filetype-wave {
100
+ background-image: url(../images/filetypes/document-music.png);
101
+ }
102
+ .filetype-mpeg, .filetype-mpg, .filetype-mpe, .filetype-qt, .filetype-mov, .filetype-mxu, .filetype-avi, .filetype-movie, .filetype-mp4, .filetype-divx {
103
+ background-image: url(../images/filetypes/document-film.png);
104
+ }
105
+ .filetype-zip, .filetype-gz, .filetype-rar, .filetype-sit, .filetype-tar, .filetype-7z {
106
+ background-image: url(../images/filetypes/document-zipper.png);
107
+ }
108
+ .filetype-xls, .filetype-tsv, .filetype-csv {
109
+ background-image: url(../images/filetypes/document-excel.png);
110
+ }
111
+ .filetype-doc {
112
+ background-image: url(../images/filetypes/document-word-text.png);
113
+ }
114
+ .filetype-ai {
115
+ background-image: url(../images/filetypes/document-illustrator.png);
116
+ }
117
+ .filetype-swf {
118
+ background-image: url(../images/filetypes/document-flash-movie.png);
119
+ }
120
+ .filetype-eps, .filetype-ps, .filetype-bmp, .filetype-gif, .filetype-ief, .filetype-jpeg, .filetype-jpg, .filetype-jpe, .filetype-png, .filetype-tiff, .filetype-tif, .filetype-djv, .filetype-wbmp, .filetype-ras, .filetype-pnm, .filetype-pbm, .filetype-pgm, .filetype-ppm, .filetype-rgb, .filetype-xbm, .filetype-xpm, .filetype-xwd {
121
+ background-image: url(../images/filetypes/document-image.png);
122
+ }
123
+ .filetype-psd {
124
+ background-image: url(../images/filetypes/document-photoshop.png);
125
+ }
126
+ .filetype-ppt {
127
+ background-image: url(../images/filetypes/document-powerpoint.png);
128
+ }
129
+ .filetype-js, .filetype-css, .filetype-as, .filetype-htm, .filetype-htaccess, .filetype-sql, .filetype-html, .filetype-php, .filetype-xml, .filetype-xsl {
130
+ background-image: url(../images/filetypes/document-code.png);
131
+ }
132
+ .filetype-rtx, .filetype-rtf {
133
+ background-image: url(../images/filetypes/document-text-image.png);
134
+ }
135
+ .filetype-txt {
136
+ background-image: url(../images/filetypes/document-text.png);
137
+ }
assets/images/ajax-loader.gif ADDED
Binary file
assets/images/cross.png ADDED
Binary file
assets/images/download_count_head.png ADDED
Binary file
assets/images/downloads-icon.gif ADDED
Binary file
assets/images/featured_head.png ADDED
Binary file
assets/images/filetypes/document-code.png ADDED
Binary file
assets/images/filetypes/document-excel.png ADDED
Binary file
assets/images/filetypes/document-film.png ADDED
Binary file
assets/images/filetypes/document-flash-movie.png ADDED
Binary file
assets/images/filetypes/document-illustrator.png ADDED
Binary file
assets/images/filetypes/document-image.png ADDED
Binary file
assets/images/filetypes/document-music.png ADDED
Binary file
assets/images/filetypes/document-pdf.png ADDED
Binary file
assets/images/filetypes/document-photoshop.png ADDED
Binary file
assets/images/filetypes/document-powerpoint.png ADDED
Binary file
assets/images/filetypes/document-text-image.png ADDED
Binary file
assets/images/filetypes/document-text.png ADDED
Binary file
assets/images/filetypes/document-word-text.png ADDED
Binary file
assets/images/filetypes/document-zipper.png ADDED
Binary file
assets/images/filetypes/document.png ADDED
Binary file
assets/images/filetypes/readme.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Fugue Icons
2
+
3
+ Copyright (C) 2010 Yusuke Kamiyamane. All rights reserved.
4
+ The icons are licensed under a Creative Commons Attribution
5
+ 3.0 license. <http://creativecommons.org/licenses/by/3.0/>
6
+
7
+ If you can't or don't want to provide a link back, please
8
+ purchase a royalty-free license. <http://pinvoke.com/>
9
+
10
+ I'm unavailable for custom icon design work. But your
11
+ suggestions are always welcome!
12
+ <mailto:yusuke.kamiyamane@gmail.com>
13
+
14
+ ------------------------------------------------------------
15
+
16
+ - geotag
17
+
18
+ Copyright (C) Geotag Icon Project. All rights reserved.
19
+ <http://www.geotagicons.com/>
20
+
21
+ Geotag icon is licensed under a Creative Commons
22
+ Attribution-Share Alike 3.0 license or LGPL.
23
+ <http://creativecommons.org/licenses/by-sa/3.0/>
24
+ <http://opensource.org/licenses/lgpl-license.php>
25
+
26
+ - language
27
+
28
+ Copyright (C) Language Icon Project. All rights reserved.
29
+ <http://www.languageicon.org/>
30
+
31
+ Language icon is licensed under a Creative Commons
32
+ Attribution-Share Alike 3.0 license.
33
+ <http://creativecommons.org/licenses/by-sa/3.0/>
34
+
35
+ - open-share
36
+
37
+ Copyright (C) Open Share Icon Project. All rights reserved.
38
+ <http://www.openshareicons.com/>
39
+
40
+ Open Share icon is licensed under a Creative Commons
41
+ Attribution-Share Alike 3.0 license.
42
+ <http://creativecommons.org/licenses/by-sa/3.0/>
43
+
44
+ - opml
45
+
46
+ Copyright (C) OPML Icon Project. All rights reserved.
47
+ <http://opmlicons.com/>
48
+
49
+ OPML icon is licensed under a Creative Commons
50
+ Attribution-Share Alike 2.5 license.
51
+ <http://creativecommons.org/licenses/by-sa/2.5/>
52
+
53
+ - share
54
+
55
+ Copyright (C) Share Icon Project. All rights reserved.
56
+ <http://shareicons.com/>
57
+
58
+ Share icon is licensed under a GPL or LGPL or BSD or
59
+ Creative Commons Attribution 2.5 license.
60
+ <http://opensource.org/licenses/gpl-license.php>
61
+ <http://opensource.org/licenses/lgpl-license.php>
62
+ <http://opensource.org/licenses/bsd-license.php>
63
+ <http://creativecommons.org/licenses/by/2.5/>
assets/images/folder-horizontal-open.png ADDED
Binary file
assets/images/folder-horizontal.png ADDED
Binary file
assets/images/media-button-download.gif ADDED
Binary file
assets/images/member_head.png ADDED
Binary file
assets/images/menu_icon.png ADDED
Binary file
assets/images/on.png ADDED
Binary file
assets/images/placeholder.png ADDED
Binary file
assets/js/blockui.js ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery blockUI plugin
3
+ * Version 2.39 (23-MAY-2011)
4
+ * @requires jQuery v1.2.3 or later
5
+ *
6
+ * Examples at: http://malsup.com/jquery/block/
7
+ * Copyright (c) 2007-2010 M. Alsup
8
+ * Dual licensed under the MIT and GPL licenses:
9
+ * http://www.opensource.org/licenses/mit-license.php
10
+ * http://www.gnu.org/licenses/gpl.html
11
+ *
12
+ * Thanks to Amir-Hossein Sobhi for some excellent contributions!
13
+ */
14
+
15
+ ;(function($) {
16
+
17
+ if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) {
18
+ alert('blockUI requires jQuery v1.2.3 or later! You are using v' + $.fn.jquery);
19
+ return;
20
+ }
21
+
22
+ $.fn._fadeIn = $.fn.fadeIn;
23
+
24
+ var noOp = function() {};
25
+
26
+ // this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
27
+ // retarded userAgent strings on Vista)
28
+ var mode = document.documentMode || 0;
29
+ var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8);
30
+ var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode;
31
+
32
+ // global $ methods for blocking/unblocking the entire page
33
+ $.blockUI = function(opts) { install(window, opts); };
34
+ $.unblockUI = function(opts) { remove(window, opts); };
35
+
36
+ // convenience method for quick growl-like notifications (http://www.google.com/search?q=growl)
37
+ $.growlUI = function(title, message, timeout, onClose) {
38
+ var $m = $('<div class="growlUI"></div>');
39
+ if (title) $m.append('<h1>'+title+'</h1>');
40
+ if (message) $m.append('<h2>'+message+'</h2>');
41
+ if (timeout == undefined) timeout = 3000;
42
+ $.blockUI({
43
+ message: $m, fadeIn: 700, fadeOut: 1000, centerY: false,
44
+ timeout: timeout, showOverlay: false,
45
+ onUnblock: onClose,
46
+ css: $.blockUI.defaults.growlCSS
47
+ });
48
+ };
49
+
50
+ // plugin method for blocking element content
51
+ $.fn.block = function(opts) {
52
+ return this.unblock({ fadeOut: 0 }).each(function() {
53
+ if ($.css(this,'position') == 'static')
54
+ this.style.position = 'relative';
55
+ if ($.browser.msie)
56
+ this.style.zoom = 1; // force 'hasLayout'
57
+ install(this, opts);
58
+ });
59
+ };
60
+
61
+ // plugin method for unblocking element content
62
+ $.fn.unblock = function(opts) {
63
+ return this.each(function() {
64
+ remove(this, opts);
65
+ });
66
+ };
67
+
68
+ $.blockUI.version = 2.39; // 2nd generation blocking at no extra cost!
69
+
70
+ // override these in your code to change the default behavior and style
71
+ $.blockUI.defaults = {
72
+ // message displayed when blocking (use null for no message)
73
+ message: '<h1>Please wait...</h1>',
74
+
75
+ title: null, // title string; only used when theme == true
76
+ draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded)
77
+
78
+ theme: false, // set to true to use with jQuery UI themes
79
+
80
+ // styles for the message when blocking; if you wish to disable
81
+ // these and use an external stylesheet then do this in your code:
82
+ // $.blockUI.defaults.css = {};
83
+ css: {
84
+ padding: 0,
85
+ margin: 0,
86
+ width: '30%',
87
+ top: '40%',
88
+ left: '35%',
89
+ textAlign: 'center',
90
+ color: '#000',
91
+ border: '3px solid #aaa',
92
+ backgroundColor:'#fff',
93
+ cursor: 'wait'
94
+ },
95
+
96
+ // minimal style set used when themes are used
97
+ themedCSS: {
98
+ width: '30%',
99
+ top: '40%',
100
+ left: '35%'
101
+ },
102
+
103
+ // styles for the overlay
104
+ overlayCSS: {
105
+ backgroundColor: '#000',
106
+ opacity: 0.6,
107
+ cursor: 'wait'
108
+ },
109
+
110
+ // styles applied when using $.growlUI
111
+ growlCSS: {
112
+ width: '350px',
113
+ top: '10px',
114
+ left: '',
115
+ right: '10px',
116
+ border: 'none',
117
+ padding: '5px',
118
+ opacity: 0.6,
119
+ cursor: 'default',
120
+ color: '#fff',
121
+ backgroundColor: '#000',
122
+ '-webkit-border-radius': '10px',
123
+ '-moz-border-radius': '10px',
124
+ 'border-radius': '10px'
125
+ },
126
+
127
+ // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
128
+ // (hat tip to Jorge H. N. de Vasconcelos)
129
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
130
+
131
+ // force usage of iframe in non-IE browsers (handy for blocking applets)
132
+ forceIframe: false,
133
+
134
+ // z-index for the blocking overlay
135
+ baseZ: 1000,
136
+
137
+ // set these to true to have the message automatically centered
138
+ centerX: true, // <-- only effects element blocking (page block controlled via css above)
139
+ centerY: true,
140
+
141
+ // allow body element to be stetched in ie6; this makes blocking look better
142
+ // on "short" pages. disable if you wish to prevent changes to the body height
143
+ allowBodyStretch: true,
144
+
145
+ // enable if you want key and mouse events to be disabled for content that is blocked
146
+ bindEvents: true,
147
+
148
+ // be default blockUI will supress tab navigation from leaving blocking content
149
+ // (if bindEvents is true)
150
+ constrainTabKey: true,
151
+
152
+ // fadeIn time in millis; set to 0 to disable fadeIn on block
153
+ fadeIn: 200,
154
+
155
+ // fadeOut time in millis; set to 0 to disable fadeOut on unblock
156
+ fadeOut: 400,
157
+
158
+ // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
159
+ timeout: 0,
160
+
161
+ // disable if you don't want to show the overlay
162
+ showOverlay: true,
163
+
164
+ // if true, focus will be placed in the first available input field when
165
+ // page blocking
166
+ focusInput: true,
167
+
168
+ // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
169
+ applyPlatformOpacityRules: true,
170
+
171
+ // callback method invoked when fadeIn has completed and blocking message is visible
172
+ onBlock: null,
173
+
174
+ // callback method invoked when unblocking has completed; the callback is
175
+ // passed the element that has been unblocked (which is the window object for page
176
+ // blocks) and the options that were passed to the unblock call:
177
+ // onUnblock(element, options)
178
+ onUnblock: null,
179
+
180
+ // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
181
+ quirksmodeOffsetHack: 4,
182
+
183
+ // class name of the message block
184
+ blockMsgClass: 'blockMsg'
185
+ };
186
+
187
+ // private data and functions follow...
188
+
189
+ var pageBlock = null;
190
+ var pageBlockEls = [];
191
+
192
+ function install(el, opts) {
193
+ var full = (el == window);
194
+ var msg = opts && opts.message !== undefined ? opts.message : undefined;
195
+ opts = $.extend({}, $.blockUI.defaults, opts || {});
196
+ opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
197
+ var css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
198
+ var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
199
+ msg = msg === undefined ? opts.message : msg;
200
+
201
+ // remove the current block (if there is one)
202
+ if (full && pageBlock)
203
+ remove(window, {fadeOut:0});
204
+
205
+ // if an existing element is being used as the blocking content then we capture
206
+ // its current place in the DOM (and current display style) so we can restore
207
+ // it when we unblock
208
+ if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
209
+ var node = msg.jquery ? msg[0] : msg;
210
+ var data = {};
211
+ $(el).data('blockUI.history', data);
212
+ data.el = node;
213
+ data.parent = node.parentNode;
214
+ data.display = node.style.display;
215
+ data.position = node.style.position;
216
+ if (data.parent)
217
+ data.parent.removeChild(node);
218
+ }
219
+
220
+ $(el).data('blockUI.onUnblock', opts.onUnblock);
221
+ var z = opts.baseZ;
222
+
223
+ // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
224
+ // layer1 is the iframe layer which is used to supress bleed through of underlying content
225
+ // layer2 is the overlay layer which has opacity and a wait cursor (by default)
226
+ // layer3 is the message content that is displayed while blocking
227
+
228
+ var lyr1 = ($.browser.msie || opts.forceIframe)
229
+ ? $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>')
230
+ : $('<div class="blockUI" style="display:none"></div>');
231
+
232
+ var lyr2 = opts.theme
233
+ ? $('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+ (z++) +';display:none"></div>')
234
+ : $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
235
+
236
+ var lyr3, s;
237
+ if (opts.theme && full) {
238
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:fixed">' +
239
+ '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
240
+ '<div class="ui-widget-content ui-dialog-content"></div>' +
241
+ '</div>';
242
+ }
243
+ else if (opts.theme) {
244
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:absolute">' +
245
+ '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || '&nbsp;')+'</div>' +
246
+ '<div class="ui-widget-content ui-dialog-content"></div>' +
247
+ '</div>';
248
+ }
249
+ else if (full) {
250
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage" style="z-index:'+(z+10)+';display:none;position:fixed"></div>';
251
+ }
252
+ else {
253
+ s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement" style="z-index:'+(z+10)+';display:none;position:absolute"></div>';
254
+ }
255
+ lyr3 = $(s);
256
+
257
+ // if we have a message, style it
258
+ if (msg) {
259
+ if (opts.theme) {
260
+ lyr3.css(themedCSS);
261
+ lyr3.addClass('ui-widget-content');
262
+ }
263
+ else
264
+ lyr3.css(css);
265
+ }
266
+
267
+ // style the overlay
268
+ if (!opts.theme && (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform))))
269
+ lyr2.css(opts.overlayCSS);
270
+ lyr2.css('position', full ? 'fixed' : 'absolute');
271
+
272
+ // make iframe layer transparent in IE
273
+ if ($.browser.msie || opts.forceIframe)
274
+ lyr1.css('opacity',0.0);
275
+
276
+ //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
277
+ var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
278
+ $.each(layers, function() {
279
+ this.appendTo($par);
280
+ });
281
+
282
+ if (opts.theme && opts.draggable && $.fn.draggable) {
283
+ lyr3.draggable({
284
+ handle: '.ui-dialog-titlebar',
285
+ cancel: 'li'
286
+ });
287
+ }
288
+
289
+ // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
290
+ var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
291
+ if (ie6 || expr) {
292
+ // give body 100% height
293
+ if (full && opts.allowBodyStretch && $.boxModel)
294
+ $('html,body').css('height','100%');
295
+
296
+ // fix ie6 issue when blocked element has a border width
297
+ if ((ie6 || !$.boxModel) && !full) {
298
+ var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
299
+ var fixT = t ? '(0 - '+t+')' : 0;
300
+ var fixL = l ? '(0 - '+l+')' : 0;
301
+ }
302
+
303
+ // simulate fixed position
304
+ $.each([lyr1,lyr2,lyr3], function(i,o) {
305
+ var s = o[0].style;
306
+ s.position = 'absolute';
307
+ if (i < 2) {
308
+ full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"')
309
+ : s.setExpression('height','this.parentNode.offsetHeight + "px"');
310
+ full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
311
+ : s.setExpression('width','this.parentNode.offsetWidth + "px"');
312
+ if (fixL) s.setExpression('left', fixL);
313
+ if (fixT) s.setExpression('top', fixT);
314
+ }
315
+ else if (opts.centerY) {
316
+ if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
317
+ s.marginTop = 0;
318
+ }
319
+ else if (!opts.centerY && full) {
320
+ var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0;
321
+ var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
322
+ s.setExpression('top',expression);
323
+ }
324
+ });
325
+ }
326
+
327
+ // show the message
328
+ if (msg) {
329
+ if (opts.theme)
330
+ lyr3.find('.ui-widget-content').append(msg);
331
+ else
332
+ lyr3.append(msg);
333
+ if (msg.jquery || msg.nodeType)
334
+ $(msg).show();
335
+ }
336
+
337
+ if (($.browser.msie || opts.forceIframe) && opts.showOverlay)
338
+ lyr1.show(); // opacity is zero
339
+ if (opts.fadeIn) {
340
+ var cb = opts.onBlock ? opts.onBlock : noOp;
341
+ var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
342
+ var cb2 = msg ? cb : noOp;
343
+ if (opts.showOverlay)
344
+ lyr2._fadeIn(opts.fadeIn, cb1);
345
+ if (msg)
346
+ lyr3._fadeIn(opts.fadeIn, cb2);
347
+ }
348
+ else {
349
+ if (opts.showOverlay)
350
+ lyr2.show();
351
+ if (msg)
352
+ lyr3.show();
353
+ if (opts.onBlock)
354
+ opts.onBlock();
355
+ }
356
+
357
+ // bind key and mouse events
358
+ bind(1, el, opts);
359
+
360
+ if (full) {
361
+ pageBlock = lyr3[0];
362
+ pageBlockEls = $(':input:enabled:visible',pageBlock);
363
+ if (opts.focusInput)
364
+ setTimeout(focus, 20);
365
+ }
366
+ else
367
+ center(lyr3[0], opts.centerX, opts.centerY);
368
+
369
+ if (opts.timeout) {
370
+ // auto-unblock
371
+ var to = setTimeout(function() {
372
+ full ? $.unblockUI(opts) : $(el).unblock(opts);
373
+ }, opts.timeout);
374
+ $(el).data('blockUI.timeout', to);
375
+ }
376
+ };
377
+
378
+ // remove the block
379
+ function remove(el, opts) {
380
+ var full = (el == window);
381
+ var $el = $(el);
382
+ var data = $el.data('blockUI.history');
383
+ var to = $el.data('blockUI.timeout');
384
+ if (to) {
385
+ clearTimeout(to);
386
+ $el.removeData('blockUI.timeout');
387
+ }
388
+ opts = $.extend({}, $.blockUI.defaults, opts || {});
389
+ bind(0, el, opts); // unbind events
390
+
391
+ if (opts.onUnblock === null) {
392
+ opts.onUnblock = $el.data('blockUI.onUnblock');
393
+ $el.removeData('blockUI.onUnblock');
394
+ }
395
+
396
+ var els;
397
+ if (full) // crazy selector to handle odd field errors in ie6/7
398
+ els = $('body').children().filter('.blockUI').add('body > .blockUI');
399
+ else
400
+ els = $('.blockUI', el);
401
+
402
+ if (full)
403
+ pageBlock = pageBlockEls = null;
404
+
405
+ if (opts.fadeOut) {
406
+ els.fadeOut(opts.fadeOut);
407
+ setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut);
408
+ }
409
+ else
410
+ reset(els, data, opts, el);
411
+ };
412
+
413
+ // move blocking element back into the DOM where it started
414
+ function reset(els,data,opts,el) {
415
+ els.each(function(i,o) {
416
+ // remove via DOM calls so we don't lose event handlers
417
+ if (this.parentNode)
418
+ this.parentNode.removeChild(this);
419
+ });
420
+
421
+ if (data && data.el) {
422
+ data.el.style.display = data.display;
423
+ data.el.style.position = data.position;
424
+ if (data.parent)
425
+ data.parent.appendChild(data.el);
426
+ $(el).removeData('blockUI.history');
427
+ }
428
+
429
+ if (typeof opts.onUnblock == 'function')
430
+ opts.onUnblock(el,opts);
431
+ };
432
+
433
+ // bind/unbind the handler
434
+ function bind(b, el, opts) {
435
+ var full = el == window, $el = $(el);
436
+
437
+ // don't bother unbinding if there is nothing to unbind
438
+ if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
439
+ return;
440
+ if (!full)
441
+ $el.data('blockUI.isBlocked', b);
442
+
443
+ // don't bind events when overlay is not in use or if bindEvents is false
444
+ if (!opts.bindEvents || (b && !opts.showOverlay))
445
+ return;
446
+
447
+ // bind anchors and inputs for mouse and key events
448
+ var events = 'mousedown mouseup keydown keypress';
449
+ b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler);
450
+
451
+ // former impl...
452
+ // var $e = $('a,:input');
453
+ // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
454
+ };
455
+
456
+ // event handler to suppress keyboard/mouse events when blocking
457
+ function handler(e) {
458
+ // allow tab navigation (conditionally)
459
+ if (e.keyCode && e.keyCode == 9) {
460
+ if (pageBlock && e.data.constrainTabKey) {
461
+ var els = pageBlockEls;
462
+ var fwd = !e.shiftKey && e.target === els[els.length-1];
463
+ var back = e.shiftKey && e.target === els[0];
464
+ if (fwd || back) {
465
+ setTimeout(function(){focus(back)},10);
466
+ return false;
467
+ }
468
+ }
469
+ }
470
+ var opts = e.data;
471
+ // allow events within the message content
472
+ if ($(e.target).parents('div.' + opts.blockMsgClass).length > 0)
473
+ return true;
474
+
475
+ // allow events for content that is not being blocked
476
+ return $(e.target).parents().children().filter('div.blockUI').length == 0;
477
+ };
478
+
479
+ function focus(back) {
480
+ if (!pageBlockEls)
481
+ return;
482
+ var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
483
+ if (e)
484
+ e.focus();
485
+ };
486
+
487
+ function center(el, x, y) {
488
+ var p = el.parentNode, s = el.style;
489
+ var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
490
+ var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
491
+ if (x) s.left = l > 0 ? (l+'px') : '0';
492
+ if (y) s.top = t > 0 ? (t+'px') : '0';
493
+ };
494
+
495
+ function sz(el, p) {
496
+ return parseInt($.css(el,p))||0;
497
+ };
498
+
499
+ })(jQuery);
assets/js/blockui.min.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery blockUI plugin
3
+ * Version 2.39 (23-MAY-2011)
4
+ * @requires jQuery v1.2.3 or later
5
+ *
6
+ * Examples at: http://malsup.com/jquery/block/
7
+ * Copyright (c) 2007-2010 M. Alsup
8
+ * Dual licensed under the MIT and GPL licenses:
9
+ * http://www.opensource.org/licenses/mit-license.php
10
+ * http://www.gnu.org/licenses/gpl.html
11
+ *
12
+ * Thanks to Amir-Hossein Sobhi for some excellent contributions!
13
+ */(function(a){function h(c,h){var j=c==window,l=h&&h.message!==undefined?h.message:undefined;h=a.extend({},a.blockUI.defaults,h||{});h.overlayCSS=a.extend({},a.blockUI.defaults.overlayCSS,h.overlayCSS||{});var p=a.extend({},a.blockUI.defaults.css,h.css||{}),q=a.extend({},a.blockUI.defaults.themedCSS,h.themedCSS||{});l=l===undefined?h.message:l;j&&f&&i(window,{fadeOut:0});if(l&&typeof l!="string"&&(l.parentNode||l.jquery)){var r=l.jquery?l[0]:l,s={};a(c).data("blockUI.history",s);s.el=r;s.parent=r.parentNode;s.display=r.style.display;s.position=r.style.position;s.parent&&s.parent.removeChild(r)}a(c).data("blockUI.onUnblock",h.onUnblock);var t=h.baseZ,u=a.browser.msie||h.forceIframe?a('<iframe class="blockUI" style="z-index:'+t++ +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+h.iframeSrc+'"></iframe>'):a('<div class="blockUI" style="display:none"></div>'),v=h.theme?a('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+t++ +';display:none"></div>'):a('<div class="blockUI blockOverlay" style="z-index:'+t++ +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>'),w,x;h.theme&&j?x='<div class="blockUI '+h.blockMsgClass+' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(t+10)+';display:none;position:fixed">'+'<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(h.title||"&nbsp;")+"</div>"+'<div class="ui-widget-content ui-dialog-content"></div>'+"</div>":h.theme?x='<div class="blockUI '+h.blockMsgClass+' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(t+10)+';display:none;position:absolute">'+'<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(h.title||"&nbsp;")+"</div>"+'<div class="ui-widget-content ui-dialog-content"></div>'+"</div>":j?x='<div class="blockUI '+h.blockMsgClass+' blockPage" style="z-index:'+(t+10)+';display:none;position:fixed"></div>':x='<div class="blockUI '+h.blockMsgClass+' blockElement" style="z-index:'+(t+10)+';display:none;position:absolute"></div>';w=a(x);if(l)if(h.theme){w.css(q);w.addClass("ui-widget-content")}else w.css(p);!h.theme&&(!h.applyPlatformOpacityRules||!a.browser.mozilla||!/Linux/.test(navigator.platform))&&v.css(h.overlayCSS);v.css("position",j?"fixed":"absolute");(a.browser.msie||h.forceIframe)&&u.css("opacity",0);var y=[u,v,w],z=j?a("body"):a(c);a.each(y,function(){this.appendTo(z)});h.theme&&h.draggable&&a.fn.draggable&&w.draggable({handle:".ui-dialog-titlebar",cancel:"li"});var A=d&&(!a.boxModel||a("object,embed",j?null:c).length>0);if(e||A){j&&h.allowBodyStretch&&a.boxModel&&a("html,body").css("height","100%");if((e||!a.boxModel)&&!j)var B=o(c,"borderTopWidth"),C=o(c,"borderLeftWidth"),D=B?"(0 - "+B+")":0,E=C?"(0 - "+C+")":0;a.each([u,v,w],function(a,b){var c=b[0].style;c.position="absolute";if(a<2){j?c.setExpression("height","Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:"+h.quirksmodeOffsetHack+') + "px"'):c.setExpression("height",'this.parentNode.offsetHeight + "px"');j?c.setExpression("width",'jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'):c.setExpression("width",'this.parentNode.offsetWidth + "px"');E&&c.setExpression("left",E);D&&c.setExpression("top",D)}else if(h.centerY){j&&c.setExpression("top",'(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');c.marginTop=0}else if(!h.centerY&&j){var d=h.css&&h.css.top?parseInt(h.css.top):0,e="((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "+d+') + "px"';c.setExpression("top",e)}})}if(l){h.theme?w.find(".ui-widget-content").append(l):w.append(l);(l.jquery||l.nodeType)&&a(l).show()}(a.browser.msie||h.forceIframe)&&h.showOverlay&&u.show();if(h.fadeIn){var F=h.onBlock?h.onBlock:b,G=h.showOverlay&&!l?F:b,H=l?F:b;h.showOverlay&&v._fadeIn(h.fadeIn,G);l&&w._fadeIn(h.fadeIn,H)}else{h.showOverlay&&v.show();l&&w.show();h.onBlock&&h.onBlock()}k(1,c,h);if(j){f=w[0];g=a(":input:enabled:visible",f);h.focusInput&&setTimeout(m,20)}else n(w[0],h.centerX,h.centerY);if(h.timeout){var I=setTimeout(function(){j?a.unblockUI(h):a(c).unblock(h)},h.timeout);a(c).data("blockUI.timeout",I)}}function i(b,c){var d=b==window,e=a(b),h=e.data("blockUI.history"),i=e.data("blockUI.timeout");if(i){clearTimeout(i);e.removeData("blockUI.timeout")}c=a.extend({},a.blockUI.defaults,c||{});k(0,b,c);if(c.onUnblock===null){c.onUnblock=e.data("blockUI.onUnblock");e.removeData("blockUI.onUnblock")}var l;d?l=a("body").children().filter(".blockUI").add("body > .blockUI"):l=a(".blockUI",b);d&&(f=g=null);if(c.fadeOut){l.fadeOut(c.fadeOut);setTimeout(function(){j(l,h,c,b)},c.fadeOut)}else j(l,h,c,b)}function j(b,c,d,e){b.each(function(a,b){this.parentNode&&this.parentNode.removeChild(this)});if(c&&c.el){c.el.style.display=c.display;c.el.style.position=c.position;c.parent&&c.parent.appendChild(c.el);a(e).removeData("blockUI.history")}typeof d.onUnblock=="function"&&d.onUnblock(e,d)}function k(b,c,d){var e=c==window,g=a(c);if(!b&&(e&&!f||!e&&!g.data("blockUI.isBlocked")))return;e||g.data("blockUI.isBlocked",b);if(!d.bindEvents||b&&!d.showOverlay)return;var h="mousedown mouseup keydown keypress";b?a(document).bind(h,d,l):a(document).unbind(h,l)}function l(b){if(b.keyCode&&b.keyCode==9&&f&&b.data.constrainTabKey){var c=g,d=!b.shiftKey&&b.target===c[c.length-1],e=b.shiftKey&&b.target===c[0];if(d||e){setTimeout(function(){m(e)},10);return!1}}var h=b.data;return a(b.target).parents("div."+h.blockMsgClass).length>0?!0:a(b.target).parents().children().filter("div.blockUI").length==0}function m(a){if(!g)return;var b=g[a===!0?g.length-1:0];b&&b.focus()}function n(a,b,c){var d=a.parentNode,e=a.style,f=(d.offsetWidth-a.offsetWidth)/2-o(d,"borderLeftWidth"),g=(d.offsetHeight-a.offsetHeight)/2-o(d,"borderTopWidth");b&&(e.left=f>0?f+"px":"0");c&&(e.top=g>0?g+"px":"0")}function o(b,c){return parseInt(a.css(b,c))||0}if(/1\.(0|1|2)\.(0|1|2)/.test(a.fn.jquery)||/^1.1/.test(a.fn.jquery)){alert("blockUI requires jQuery v1.2.3 or later! You are using v"+a.fn.jquery);return}a.fn._fadeIn=a.fn.fadeIn;var b=function(){},c=document.documentMode||0,d=a.browser.msie&&(a.browser.version<8&&!c||c<8),e=a.browser.msie&&/MSIE 6.0/.test(navigator.userAgent)&&!c;a.blockUI=function(a){h(window,a)};a.unblockUI=function(a){i(window,a)};a.growlUI=function(b,c,d,e){var f=a('<div class="growlUI"></div>');b&&f.append("<h1>"+b+"</h1>");c&&f.append("<h2>"+c+"</h2>");d==undefined&&(d=3e3);a.blockUI({message:f,fadeIn:700,fadeOut:1e3,centerY:!1,timeout:d,showOverlay:!1,onUnblock:e,css:a.blockUI.defaults.growlCSS})};a.fn.block=function(b){return this.unblock({fadeOut:0}).each(function(){a.css(this,"position")=="static"&&(this.style.position="relative");a.browser.msie&&(this.style.zoom=1);h(this,b)})};a.fn.unblock=function(a){return this.each(function(){i(this,a)})};a.blockUI.version=2.39;a.blockUI.defaults={message:"<h1>Please wait...</h1>",title:null,draggable:!0,theme:!1,css:{padding:0,margin:0,width:"30%",top:"40%",left:"35%",textAlign:"center",color:"#000",border:"3px solid #aaa",backgroundColor:"#fff",cursor:"wait"},themedCSS:{width:"30%",top:"40%",left:"35%"},overlayCSS:{backgroundColor:"#000",opacity:.6,cursor:"wait"},growlCSS:{width:"350px",top:"10px",left:"",right:"10px",border:"none",padding:"5px",opacity:.6,cursor:"default",color:"#fff",backgroundColor:"#000","-webkit-border-radius":"10px","-moz-border-radius":"10px","border-radius":"10px"},iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank",forceIframe:!1,baseZ:1e3,centerX:!0,centerY:!0,allowBodyStretch:!0,bindEvents:!0,constrainTabKey:!0,fadeIn:200,fadeOut:400,timeout:0,showOverlay:!0,focusInput:!0,applyPlatformOpacityRules:!0,onBlock:null,onUnblock:null,quirksmodeOffsetHack:4,blockMsgClass:"blockMsg"};var f=null,g=[]})(jQuery);
assets/js/chosen/chosen-sprite.png ADDED
Binary file
assets/js/chosen/chosen-sprite@2x.png ADDED
Binary file
assets/js/chosen/chosen.css ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* @group Base */
2
+ .chzn-container {
3
+ font-size: 13px;
4
+ position: relative;
5
+ display: inline-block;
6
+ zoom: 1;
7
+ *display: inline;
8
+ }
9
+ .chzn-container .chzn-drop {
10
+ background: #fff;
11
+ border: 1px solid #aaa;
12
+ border-top: 0;
13
+ position: absolute;
14
+ top: 29px;
15
+ left: 0;
16
+ -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
17
+ -moz-box-shadow : 0 4px 5px rgba(0,0,0,.15);
18
+ box-shadow : 0 4px 5px rgba(0,0,0,.15);
19
+ z-index: 1010;
20
+ }
21
+ /* @end */
22
+
23
+ /* @group Single Chosen */
24
+ .chzn-container-single .chzn-single {
25
+ background-color: #ffffff;
26
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0 );
27
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
28
+ background-image: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
29
+ background-image: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
30
+ background-image: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
31
+ background-image: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
32
+ -webkit-border-radius: 5px;
33
+ -moz-border-radius : 5px;
34
+ border-radius : 5px;
35
+ -moz-background-clip : padding;
36
+ -webkit-background-clip: padding-box;
37
+ background-clip : padding-box;
38
+ border: 1px solid #aaaaaa;
39
+ -webkit-box-shadow: 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
40
+ -moz-box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
41
+ box-shadow : 0 0 3px #ffffff inset, 0 1px 1px rgba(0,0,0,0.1);
42
+ display: block;
43
+ overflow: hidden;
44
+ white-space: nowrap;
45
+ position: relative;
46
+ height: 23px;
47
+ line-height: 24px;
48
+ padding: 0 0 0 8px;
49
+ color: #444444;
50
+ text-decoration: none;
51
+ }
52
+ .chzn-container-single .chzn-default {
53
+ color: #999;
54
+ }
55
+ .chzn-container-single .chzn-single span {
56
+ margin-right: 26px;
57
+ display: block;
58
+ overflow: hidden;
59
+ white-space: nowrap;
60
+ -o-text-overflow: ellipsis;
61
+ -ms-text-overflow: ellipsis;
62
+ text-overflow: ellipsis;
63
+ }
64
+ .chzn-container-single .chzn-single abbr {
65
+ display: block;
66
+ position: absolute;
67
+ right: 26px;
68
+ top: 6px;
69
+ width: 12px;
70
+ height: 12px;
71
+ font-size: 1px;
72
+ background: url('chosen-sprite.png') -42px 1px no-repeat;
73
+ }
74
+ .chzn-container-single .chzn-single abbr:hover {
75
+ background-position: -42px -10px;
76
+ }
77
+ .chzn-container-single.chzn-disabled .chzn-single abbr:hover {
78
+ background-position: -42px -10px;
79
+ }
80
+ .chzn-container-single .chzn-single div {
81
+ position: absolute;
82
+ right: 0;
83
+ top: 0;
84
+ display: block;
85
+ height: 100%;
86
+ width: 18px;
87
+ }
88
+ .chzn-container-single .chzn-single div b {
89
+ background: url('chosen-sprite.png') no-repeat 0px 2px;
90
+ display: block;
91
+ width: 100%;
92
+ height: 100%;
93
+ }
94
+ .chzn-container-single .chzn-search {
95
+ padding: 3px 4px;
96
+ position: relative;
97
+ margin: 0;
98
+ white-space: nowrap;
99
+ z-index: 1010;
100
+ }
101
+ .chzn-container-single .chzn-search input {
102
+ background: #fff url('chosen-sprite.png') no-repeat 100% -20px;
103
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
104
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
105
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
106
+ background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
107
+ background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(#eeeeee 1%, #ffffff 15%);
108
+ margin: 1px 0;
109
+ padding: 4px 20px 4px 5px;
110
+ outline: 0;
111
+ border: 1px solid #aaa;
112
+ font-family: sans-serif;
113
+ font-size: 1em;
114
+ }
115
+ .chzn-container-single .chzn-drop {
116
+ -webkit-border-radius: 0 0 4px 4px;
117
+ -moz-border-radius : 0 0 4px 4px;
118
+ border-radius : 0 0 4px 4px;
119
+ -moz-background-clip : padding;
120
+ -webkit-background-clip: padding-box;
121
+ background-clip : padding-box;
122
+ }
123
+ /* @end */
124
+
125
+ .chzn-container-single-nosearch .chzn-search input {
126
+ position: absolute;
127
+ left: -9000px;
128
+ }
129
+
130
+ /* @group Multi Chosen */
131
+ .chzn-container-multi .chzn-choices {
132
+ background-color: #fff;
133
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
134
+ background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
135
+ background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
136
+ background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
137
+ background-image: linear-gradient(#eeeeee 1%, #ffffff 15%);
138
+ border: 1px solid #aaa;
139
+ margin: 0;
140
+ padding: 0;
141
+ cursor: text;
142
+ overflow: hidden;
143
+ height: auto !important;
144
+ height: 1%;
145
+ position: relative;
146
+ }
147
+ .chzn-container-multi .chzn-choices li {
148
+ float: left;
149
+ list-style: none;
150
+ }
151
+ .chzn-container-multi .chzn-choices .search-field {
152
+ white-space: nowrap;
153
+ margin: 0;
154
+ padding: 0;
155
+ }
156
+ .chzn-container-multi .chzn-choices .search-field input {
157
+ color: #666;
158
+ background: transparent !important;
159
+ border: 0 !important;
160
+ font-family: sans-serif;
161
+ font-size: 100%;
162
+ height: 15px;
163
+ padding: 5px;
164
+ margin: 1px 0;
165
+ outline: 0;
166
+ -webkit-box-shadow: none;
167
+ -moz-box-shadow : none;
168
+ box-shadow : none;
169
+ }
170
+ .chzn-container-multi .chzn-choices .search-field .default {
171
+ color: #999;
172
+ }
173
+ .chzn-container-multi .chzn-choices .search-choice {
174
+ -webkit-border-radius: 3px;
175
+ -moz-border-radius : 3px;
176
+ border-radius : 3px;
177
+ -moz-background-clip : padding;
178
+ -webkit-background-clip: padding-box;
179
+ background-clip : padding-box;
180
+ background-color: #e4e4e4;
181
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
182
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
183
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
184
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
185
+ background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
186
+ background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
187
+ -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
188
+ -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
189
+ box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
190
+ color: #333;
191
+ border: 1px solid #aaaaaa;
192
+ line-height: 13px;
193
+ padding: 3px 20px 3px 5px;
194
+ margin: 3px 0 3px 5px;
195
+ position: relative;
196
+ cursor: default;
197
+ }
198
+ .chzn-container-multi .chzn-choices .search-choice.search-choice-disabled {
199
+ background-color: #e4e4e4;
200
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
201
+ background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
202
+ background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
203
+ background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
204
+ background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
205
+ background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
206
+ background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
207
+ color: #666;
208
+ border: 1px solid #cccccc;
209
+ padding-right: 5px;
210
+ }
211
+ .chzn-container-multi .chzn-choices .search-choice-focus {
212
+ background: #d4d4d4;
213
+ }
214
+ .chzn-container-multi .chzn-choices .search-choice .search-choice-close {
215
+ display: block;
216
+ position: absolute;
217
+ right: 3px;
218
+ top: 4px;
219
+ width: 12px;
220
+ height: 12px;
221
+ font-size: 1px;
222
+ background: url('chosen-sprite.png') -42px 1px no-repeat;
223
+ }
224
+ .chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
225
+ background-position: -42px -10px;
226
+ }
227
+ .chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
228
+ background-position: -42px -10px;
229
+ }
230
+ /* @end */
231
+
232
+ /* @group Results */
233
+ .chzn-container .chzn-results {
234
+ margin: 0 4px 4px 0;
235
+ max-height: 240px;
236
+ padding: 0 0 0 4px;
237
+ position: relative;
238
+ overflow-x: hidden;
239
+ overflow-y: auto;
240
+ -webkit-overflow-scrolling: touch;
241
+ }
242
+ .chzn-container-multi .chzn-results {
243
+ margin: -1px 0 0;
244
+ padding: 0;
245
+ }
246
+ .chzn-container .chzn-results li {
247
+ display: none;
248
+ line-height: 15px;
249
+ padding: 5px 6px;
250
+ margin: 0;
251
+ list-style: none;
252
+ }
253
+ .chzn-container .chzn-results .active-result {
254
+ cursor: pointer;
255
+ display: list-item;
256
+ }
257
+ .chzn-container .chzn-results .highlighted {
258
+ background-color: #3875d7;
259
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3875d7', endColorstr='#2a62bc', GradientType=0 );
260
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
261
+ background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
262
+ background-image: -moz-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
263
+ background-image: -o-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
264
+ background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
265
+ color: #fff;
266
+ }
267
+ .chzn-container .chzn-results li em {
268
+ background: #feffde;
269
+ font-style: normal;
270
+ }
271
+ .chzn-container .chzn-results .highlighted em {
272
+ background: transparent;
273
+ }
274
+ .chzn-container .chzn-results .no-results {
275
+ background: #f4f4f4;
276
+ display: list-item;
277
+ }
278
+ .chzn-container .chzn-results .group-result {
279
+ cursor: default;
280
+ color: #999;
281
+ font-weight: bold;
282
+ }
283
+ .chzn-container .chzn-results .group-option {
284
+ padding-left: 15px;
285
+ }
286
+ .chzn-container-multi .chzn-drop .result-selected {
287
+ display: none;
288
+ }
289
+ .chzn-container .chzn-results-scroll {
290
+ background: white;
291
+ margin: 0 4px;
292
+ position: absolute;
293
+ text-align: center;
294
+ width: 321px; /* This should by dynamic with js */
295
+ z-index: 1;
296
+ }
297
+ .chzn-container .chzn-results-scroll span {
298
+ display: inline-block;
299
+ height: 17px;
300
+ text-indent: -5000px;
301
+ width: 9px;
302
+ }
303
+ .chzn-container .chzn-results-scroll-down {
304
+ bottom: 0;
305
+ }
306
+ .chzn-container .chzn-results-scroll-down span {
307
+ background: url('chosen-sprite.png') no-repeat -4px -3px;
308
+ }
309
+ .chzn-container .chzn-results-scroll-up span {
310
+ background: url('chosen-sprite.png') no-repeat -22px -3px;
311
+ }
312
+ /* @end */
313
+
314
+ /* @group Active */
315
+ .chzn-container-active .chzn-single {
316
+ -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
317
+ -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
318
+ box-shadow : 0 0 5px rgba(0,0,0,.3);
319
+ border: 1px solid #5897fb;
320
+ }
321
+ .chzn-container-active .chzn-single-with-drop {
322
+ border: 1px solid #aaa;
323
+ -webkit-box-shadow: 0 1px 0 #fff inset;
324
+ -moz-box-shadow : 0 1px 0 #fff inset;
325
+ box-shadow : 0 1px 0 #fff inset;
326
+ background-color: #eee;
327
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 );
328
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
329
+ background-image: -webkit-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
330
+ background-image: -moz-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
331
+ background-image: -o-linear-gradient(top, #eeeeee 20%, #ffffff 80%);
332
+ background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
333
+ -webkit-border-bottom-left-radius : 0;
334
+ -webkit-border-bottom-right-radius: 0;
335
+ -moz-border-radius-bottomleft : 0;
336
+ -moz-border-radius-bottomright: 0;
337
+ border-bottom-left-radius : 0;
338
+ border-bottom-right-radius: 0;
339
+ }
340
+ .chzn-container-active .chzn-single-with-drop div {
341
+ background: transparent;
342
+ border-left: none;
343
+ }
344
+ .chzn-container-active .chzn-single-with-drop div b {
345
+ background-position: -18px 2px;
346
+ }
347
+ .chzn-container-active .chzn-choices {
348
+ -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
349
+ -moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
350
+ box-shadow : 0 0 5px rgba(0,0,0,.3);
351
+ border: 1px solid #5897fb;
352
+ }
353
+ .chzn-container-active .chzn-choices .search-field input {
354
+ color: #111 !important;
355
+ }
356
+ /* @end */
357
+
358
+ /* @group Disabled Support */
359
+ .chzn-disabled {
360
+ cursor: default;
361
+ opacity:0.5 !important;
362
+ }
363
+ .chzn-disabled .chzn-single {
364
+ cursor: default;
365
+ }
366
+ .chzn-disabled .chzn-choices .search-choice .search-choice-close {
367
+ cursor: default;
368
+ }
369
+
370
+ /* @group Right to Left */
371
+ .chzn-rtl { text-align: right; }
372
+ .chzn-rtl .chzn-single { padding: 0 8px 0 0; overflow: visible; }
373
+ .chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; direction: rtl; }
374
+
375
+ .chzn-rtl .chzn-single div { left: 3px; right: auto; }
376
+ .chzn-rtl .chzn-single abbr {
377
+ left: 26px;
378
+ right: auto;
379
+ }
380
+ .chzn-rtl .chzn-choices .search-field input { direction: rtl; }
381
+ .chzn-rtl .chzn-choices li { float: right; }
382
+ .chzn-rtl .chzn-choices .search-choice { padding: 3px 5px 3px 19px; margin: 3px 5px 3px 0; }
383
+ .chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 4px; right: auto; }
384
+ .chzn-rtl.chzn-container-single .chzn-results { margin: 0 0 4px 4px; padding: 0 4px 0 0; }
385
+ .chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 15px; }
386
+ .chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
387
+ .chzn-rtl .chzn-search input {
388
+ background: #fff url('chosen-sprite.png') no-repeat -30px -20px;
389
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-gradient(linear, 0 0, 0 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
390
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
391
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
392
+ background: url('chosen-sprite.png') no-repeat -30px -20px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
393
+ background: url('chosen-sprite.png') no-repeat -30px -20px, linear-gradient(#eeeeee 1%, #ffffff 15%);
394
+ padding: 4px 5px 4px 20px;
395
+ direction: rtl;
396
+ }
397
+ .chzn-container-single.chzn-rtl .chzn-single div b {
398
+ background-position: 6px 2px;
399
+ }
400
+ .chzn-container-single.chzn-rtl .chzn-single-with-drop div b {
401
+ background-position: -12px 2px;
402
+ }
403
+ /* @end */
404
+
405
+ /* @group Retina compatibility */
406
+ @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) {
407
+ .chzn-rtl .chzn-search input, .chzn-container-single .chzn-single abbr, .chzn-container-single .chzn-single div b, .chzn-container-single .chzn-search input, .chzn-container-multi .chzn-choices .search-choice .search-choice-close, .chzn-container .chzn-results-scroll-down span, .chzn-container .chzn-results-scroll-up span {
408
+ background-image: url('chosen-sprite@2x.png') !important;
409
+ background-repeat: no-repeat !important;
410
+ background-size: 52px 37px !important;
411
+ }
412
+ }
413
+ /* @end */
assets/js/chosen/chosen.jquery.js ADDED
@@ -0,0 +1,1090 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Chosen, a Select Box Enhancer for jQuery and Protoype
2
+ // by Patrick Filler for Harvest, http://getharvest.com
3
+ //
4
+ // Version 0.9.12
5
+ // Full source at https://github.com/harvesthq/chosen
6
+ // Copyright (c) 2011 Harvest http://getharvest.com
7
+
8
+ // MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
9
+ // This file is generated by `cake build`, do not edit it by hand.
10
+ (function() {
11
+ var SelectParser;
12
+
13
+ SelectParser = (function() {
14
+
15
+ function SelectParser() {
16
+ this.options_index = 0;
17
+ this.parsed = [];
18
+ }
19
+
20
+ SelectParser.prototype.add_node = function(child) {
21
+ if (child.nodeName.toUpperCase() === "OPTGROUP") {
22
+ return this.add_group(child);
23
+ } else {
24
+ return this.add_option(child);
25
+ }
26
+ };
27
+
28
+ SelectParser.prototype.add_group = function(group) {
29
+ var group_position, option, _i, _len, _ref, _results;
30
+ group_position = this.parsed.length;
31
+ this.parsed.push({
32
+ array_index: group_position,
33
+ group: true,
34
+ label: group.label,
35
+ children: 0,
36
+ disabled: group.disabled
37
+ });
38
+ _ref = group.childNodes;
39
+ _results = [];
40
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
41
+ option = _ref[_i];
42
+ _results.push(this.add_option(option, group_position, group.disabled));
43
+ }
44
+ return _results;
45
+ };
46
+
47
+ SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
48
+ if (option.nodeName.toUpperCase() === "OPTION") {
49
+ if (option.text !== "") {
50
+ if (group_position != null) {
51
+ this.parsed[group_position].children += 1;
52
+ }
53
+ this.parsed.push({
54
+ array_index: this.parsed.length,
55
+ options_index: this.options_index,
56
+ value: option.value,
57
+ text: option.text,
58
+ html: option.innerHTML,
59
+ selected: option.selected,
60
+ disabled: group_disabled === true ? group_disabled : option.disabled,
61
+ group_array_index: group_position,
62
+ classes: option.className,
63
+ style: option.style.cssText
64
+ });
65
+ } else {
66
+ this.parsed.push({
67
+ array_index: this.parsed.length,
68
+ options_index: this.options_index,
69
+ empty: true
70
+ });
71
+ }
72
+ return this.options_index += 1;
73
+ }
74
+ };
75
+
76
+ return SelectParser;
77
+
78
+ })();
79
+
80
+ SelectParser.select_to_array = function(select) {
81
+ var child, parser, _i, _len, _ref;
82
+ parser = new SelectParser();
83
+ _ref = select.childNodes;
84
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
85
+ child = _ref[_i];
86
+ parser.add_node(child);
87
+ }
88
+ return parser.parsed;
89
+ };
90
+
91
+ this.SelectParser = SelectParser;
92
+
93
+ }).call(this);
94
+
95
+ /*
96
+ Chosen source: generate output using 'cake build'
97
+ Copyright (c) 2011 by Harvest
98
+ */
99
+
100
+
101
+ (function() {
102
+ var AbstractChosen, root;
103
+
104
+ root = this;
105
+
106
+ AbstractChosen = (function() {
107
+
108
+ function AbstractChosen(form_field, options) {
109
+ this.form_field = form_field;
110
+ this.options = options != null ? options : {};
111
+ this.is_multiple = this.form_field.multiple;
112
+ this.set_default_text();
113
+ this.set_default_values();
114
+ this.setup();
115
+ this.set_up_html();
116
+ this.register_observers();
117
+ this.finish_setup();
118
+ }
119
+
120
+ AbstractChosen.prototype.set_default_values = function() {
121
+ var _this = this;
122
+ this.click_test_action = function(evt) {
123
+ return _this.test_active_click(evt);
124
+ };
125
+ this.activate_action = function(evt) {
126
+ return _this.activate_field(evt);
127
+ };
128
+ this.active_field = false;
129
+ this.mouse_on_container = false;
130
+ this.results_showing = false;
131
+ this.result_highlighted = null;
132
+ this.result_single_selected = null;
133
+ this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
134
+ this.disable_search_threshold = this.options.disable_search_threshold || 0;
135
+ this.disable_search = this.options.disable_search || false;
136
+ this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
137
+ this.search_contains = this.options.search_contains || false;
138
+ this.choices = 0;
139
+ this.single_backstroke_delete = this.options.single_backstroke_delete || false;
140
+ this.max_selected_options = this.options.max_selected_options || Infinity;
141
+ return this.inherit_select_classes = this.options.inherit_select_classes || false;
142
+ };
143
+
144
+ AbstractChosen.prototype.set_default_text = function() {
145
+ if (this.form_field.getAttribute("data-placeholder")) {
146
+ this.default_text = this.form_field.getAttribute("data-placeholder");
147
+ } else if (this.is_multiple) {
148
+ this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || "Select Some Options";
149
+ } else {
150
+ this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || "Select an Option";
151
+ }
152
+ return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || "No results match";
153
+ };
154
+
155
+ AbstractChosen.prototype.mouse_enter = function() {
156
+ return this.mouse_on_container = true;
157
+ };
158
+
159
+ AbstractChosen.prototype.mouse_leave = function() {
160
+ return this.mouse_on_container = false;
161
+ };
162
+
163
+ AbstractChosen.prototype.input_focus = function(evt) {
164
+ var _this = this;
165
+ if (this.is_multiple) {
166
+ if (!this.active_field) {
167
+ return setTimeout((function() {
168
+ return _this.container_mousedown();
169
+ }), 50);
170
+ }
171
+ } else {
172
+ if (!this.active_field) {
173
+ return this.activate_field();
174
+ }
175
+ }
176
+ };
177
+
178
+ AbstractChosen.prototype.input_blur = function(evt) {
179
+ var _this = this;
180
+ if (!this.mouse_on_container) {
181
+ this.active_field = false;
182
+ return setTimeout((function() {
183
+ return _this.blur_test();
184
+ }), 100);
185
+ }
186
+ };
187
+
188
+ AbstractChosen.prototype.result_add_option = function(option) {
189
+ var classes, style;
190
+ if (!option.disabled) {
191
+ option.dom_id = this.container_id + "_o_" + option.array_index;
192
+ classes = option.selected && this.is_multiple ? [] : ["active-result"];
193
+ if (option.selected) {
194
+ classes.push("result-selected");
195
+ }
196
+ if (option.group_array_index != null) {
197
+ classes.push("group-option");
198
+ }
199
+ if (option.classes !== "") {
200
+ classes.push(option.classes);
201
+ }
202
+ style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : "";
203
+ return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"' + style + '>' + option.html + '</li>';
204
+ } else {
205
+ return "";
206
+ }
207
+ };
208
+
209
+ AbstractChosen.prototype.results_update_field = function() {
210
+ this.set_default_text();
211
+ if (!this.is_multiple) {
212
+ this.results_reset_cleanup();
213
+ }
214
+ this.result_clear_highlight();
215
+ this.result_single_selected = null;
216
+ return this.results_build();
217
+ };
218
+
219
+ AbstractChosen.prototype.results_toggle = function() {
220
+ if (this.results_showing) {
221
+ return this.results_hide();
222
+ } else {
223
+ return this.results_show();
224
+ }
225
+ };
226
+
227
+ AbstractChosen.prototype.results_search = function(evt) {
228
+ if (this.results_showing) {
229
+ return this.winnow_results();
230
+ } else {
231
+ return this.results_show();
232
+ }
233
+ };
234
+
235
+ AbstractChosen.prototype.keyup_checker = function(evt) {
236
+ var stroke, _ref;
237
+ stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
238
+ this.search_field_scale();
239
+ switch (stroke) {
240
+ case 8:
241
+ if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) {
242
+ return this.keydown_backstroke();
243
+ } else if (!this.pending_backstroke) {
244
+ this.result_clear_highlight();
245
+ return this.results_search();
246
+ }
247
+ break;
248
+ case 13:
249
+ evt.preventDefault();
250
+ if (this.results_showing) {
251
+ return this.result_select(evt);
252
+ }
253
+ break;
254
+ case 27:
255
+ if (this.results_showing) {
256
+ this.results_hide();
257
+ }
258
+ return true;
259
+ case 9:
260
+ case 38:
261
+ case 40:
262
+ case 16:
263
+ case 91:
264
+ case 17:
265
+ break;
266
+ default:
267
+ return this.results_search();
268
+ }
269
+ };
270
+
271
+ AbstractChosen.prototype.generate_field_id = function() {
272
+ var new_id;
273
+ new_id = this.generate_random_id();
274
+ this.form_field.id = new_id;
275
+ return new_id;
276
+ };
277
+
278
+ AbstractChosen.prototype.generate_random_char = function() {
279
+ var chars, newchar, rand;
280
+ chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
281
+ rand = Math.floor(Math.random() * chars.length);
282
+ return newchar = chars.substring(rand, rand + 1);
283
+ };
284
+
285
+ return AbstractChosen;
286
+
287
+ })();
288
+
289
+ root.AbstractChosen = AbstractChosen;
290
+
291
+ }).call(this);
292
+
293
+ /*
294
+ Chosen source: generate output using 'cake build'
295
+ Copyright (c) 2011 by Harvest
296
+ */
297
+
298
+
299
+ (function() {
300
+ var $, Chosen, get_side_border_padding, root,
301
+ __hasProp = {}.hasOwnProperty,
302
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
303
+
304
+ root = this;
305
+
306
+ $ = jQuery;
307
+
308
+ $.fn.extend({
309
+ chosen: function(options) {
310
+ var browser, match, ua;
311
+ ua = navigator.userAgent.toLowerCase();
312
+ match = /(msie) ([\w.]+)/.exec(ua) || [];
313
+ browser = {
314
+ name: match[1] || "",
315
+ version: match[2] || "0"
316
+ };
317
+ if (browser.name === "msie" && (browser.version === "6.0" || (browser.version === "7.0" && document.documentMode === 7))) {
318
+ return this;
319
+ }
320
+ return this.each(function(input_field) {
321
+ var $this;
322
+ $this = $(this);
323
+ if (!$this.hasClass("chzn-done")) {
324
+ return $this.data('chosen', new Chosen(this, options));
325
+ }
326
+ });
327
+ }
328
+ });
329
+
330
+ Chosen = (function(_super) {
331
+
332
+ __extends(Chosen, _super);
333
+
334
+ function Chosen() {
335
+ return Chosen.__super__.constructor.apply(this, arguments);
336
+ }
337
+
338
+ Chosen.prototype.setup = function() {
339
+ this.form_field_jq = $(this.form_field);
340
+ this.current_value = this.form_field_jq.val();
341
+ return this.is_rtl = this.form_field_jq.hasClass("chzn-rtl");
342
+ };
343
+
344
+ Chosen.prototype.finish_setup = function() {
345
+ return this.form_field_jq.addClass("chzn-done");
346
+ };
347
+
348
+ Chosen.prototype.set_up_html = function() {
349
+ var container_classes, container_div, container_props, dd_top, dd_width, sf_width;
350
+ this.container_id = this.form_field.id.length ? this.form_field.id.replace(/[^\w]/g, '_') : this.generate_field_id();
351
+ this.container_id += "_chzn";
352
+ container_classes = ["chzn-container"];
353
+ container_classes.push("chzn-container-" + (this.is_multiple ? "multi" : "single"));
354
+ if (this.inherit_select_classes && this.form_field.className) {
355
+ container_classes.push(this.form_field.className);
356
+ }
357
+ if (this.is_rtl) {
358
+ container_classes.push("chzn-rtl");
359
+ }
360
+ this.f_width = this.form_field_jq.outerWidth();
361
+ container_props = {
362
+ id: this.container_id,
363
+ "class": container_classes.join(' '),
364
+ style: 'width: ' + this.f_width + 'px;',
365
+ title: this.form_field.title
366
+ };
367
+ container_div = $("<div />", container_props);
368
+ if (this.is_multiple) {
369
+ container_div.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');
370
+ } else {
371
+ container_div.html('<a href="javascript:void(0)" class="chzn-single chzn-default" tabindex="-1"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>');
372
+ }
373
+ this.form_field_jq.hide().after(container_div);
374
+ this.container = $('#' + this.container_id);
375
+ this.dropdown = this.container.find('div.chzn-drop').first();
376
+ dd_top = this.container.height();
377
+ dd_width = this.f_width - get_side_border_padding(this.dropdown);
378
+ this.dropdown.css({
379
+ "width": dd_width + "px",
380
+ "top": dd_top + "px"
381
+ });
382
+ this.search_field = this.container.find('input').first();
383
+ this.search_results = this.container.find('ul.chzn-results').first();
384
+ this.search_field_scale();
385
+ this.search_no_results = this.container.find('li.no-results').first();
386
+ if (this.is_multiple) {
387
+ this.search_choices = this.container.find('ul.chzn-choices').first();
388
+ this.search_container = this.container.find('li.search-field').first();
389
+ } else {
390
+ this.search_container = this.container.find('div.chzn-search').first();
391
+ this.selected_item = this.container.find('.chzn-single').first();
392
+ sf_width = dd_width - get_side_border_padding(this.search_container) - get_side_border_padding(this.search_field);
393
+ this.search_field.css({
394
+ "width": sf_width + "px"
395
+ });
396
+ }
397
+ this.results_build();
398
+ this.set_tab_index();
399
+ return this.form_field_jq.trigger("liszt:ready", {
400
+ chosen: this
401
+ });
402
+ };
403
+
404
+ Chosen.prototype.register_observers = function() {
405
+ var _this = this;
406
+ this.container.mousedown(function(evt) {
407
+ _this.container_mousedown(evt);
408
+ });
409
+ this.container.mouseup(function(evt) {
410
+ _this.container_mouseup(evt);
411
+ });
412
+ this.container.mouseenter(function(evt) {
413
+ _this.mouse_enter(evt);
414
+ });
415
+ this.container.mouseleave(function(evt) {
416
+ _this.mouse_leave(evt);
417
+ });
418
+ this.search_results.mouseup(function(evt) {
419
+ _this.search_results_mouseup(evt);
420
+ });
421
+ this.search_results.mouseover(function(evt) {
422
+ _this.search_results_mouseover(evt);
423
+ });
424
+ this.search_results.mouseout(function(evt) {
425
+ _this.search_results_mouseout(evt);
426
+ });
427
+ this.form_field_jq.bind("liszt:updated", function(evt) {
428
+ _this.results_update_field(evt);
429
+ });
430
+ this.form_field_jq.bind("liszt:activate", function(evt) {
431
+ _this.activate_field(evt);
432
+ });
433
+ this.form_field_jq.bind("liszt:open", function(evt) {
434
+ _this.container_mousedown(evt);
435
+ });
436
+ this.search_field.blur(function(evt) {
437
+ _this.input_blur(evt);
438
+ });
439
+ this.search_field.keyup(function(evt) {
440
+ _this.keyup_checker(evt);
441
+ });
442
+ this.search_field.keydown(function(evt) {
443
+ _this.keydown_checker(evt);
444
+ });
445
+ this.search_field.focus(function(evt) {
446
+ _this.input_focus(evt);
447
+ });
448
+ if (this.is_multiple) {
449
+ return this.search_choices.click(function(evt) {
450
+ _this.choices_click(evt);
451
+ });
452
+ } else {
453
+ return this.container.click(function(evt) {
454
+ evt.preventDefault();
455
+ });
456
+ }
457
+ };
458
+
459
+ Chosen.prototype.search_field_disabled = function() {
460
+ this.is_disabled = this.form_field_jq[0].disabled;
461
+ if (this.is_disabled) {
462
+ this.container.addClass('chzn-disabled');
463
+ this.search_field[0].disabled = true;
464
+ if (!this.is_multiple) {
465
+ this.selected_item.unbind("focus", this.activate_action);
466
+ }
467
+ return this.close_field();
468
+ } else {
469
+ this.container.removeClass('chzn-disabled');
470
+ this.search_field[0].disabled = false;
471
+ if (!this.is_multiple) {
472
+ return this.selected_item.bind("focus", this.activate_action);
473
+ }
474
+ }
475
+ };
476
+
477
+ Chosen.prototype.container_mousedown = function(evt) {
478
+ var target_closelink;
479
+ if (!this.is_disabled) {
480
+ target_closelink = evt != null ? ($(evt.target)).hasClass("search-choice-close") : false;
481
+ if (evt && evt.type === "mousedown" && !this.results_showing) {
482
+ evt.preventDefault();
483
+ }
484
+ if (!this.pending_destroy_click && !target_closelink) {
485
+ if (!this.active_field) {
486
+ if (this.is_multiple) {
487
+ this.search_field.val("");
488
+ }
489
+ $(document).click(this.click_test_action);
490
+ this.results_show();
491
+ } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chzn-single").length)) {
492
+ evt.preventDefault();
493
+ this.results_toggle();
494
+ }
495
+ return this.activate_field();
496
+ } else {
497
+ return this.pending_destroy_click = false;
498
+ }
499
+ }
500
+ };
501
+
502
+ Chosen.prototype.container_mouseup = function(evt) {
503
+ if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
504
+ return this.results_reset(evt);
505
+ }
506
+ };
507
+
508
+ Chosen.prototype.blur_test = function(evt) {
509
+ if (!this.active_field && this.container.hasClass("chzn-container-active")) {
510
+ return this.close_field();
511
+ }
512
+ };
513
+
514
+ Chosen.prototype.close_field = function() {
515
+ $(document).unbind("click", this.click_test_action);
516
+ this.active_field = false;
517
+ this.results_hide();
518
+ this.container.removeClass("chzn-container-active");
519
+ this.winnow_results_clear();
520
+ this.clear_backstroke();
521
+ this.show_search_field_default();
522
+ return this.search_field_scale();
523
+ };
524
+
525
+ Chosen.prototype.activate_field = function() {
526
+ this.container.addClass("chzn-container-active");
527
+ this.active_field = true;
528
+ this.search_field.val(this.search_field.val());
529
+ return this.search_field.focus();
530
+ };
531
+
532
+ Chosen.prototype.test_active_click = function(evt) {
533
+ if ($(evt.target).parents('#' + this.container_id).length) {
534
+ return this.active_field = true;
535
+ } else {
536
+ return this.close_field();
537
+ }
538
+ };
539
+
540
+ Chosen.prototype.results_build = function() {
541
+ var content, data, _i, _len, _ref;
542
+ this.parsing = true;
543
+ this.results_data = root.SelectParser.select_to_array(this.form_field);
544
+ if (this.is_multiple && this.choices > 0) {
545
+ this.search_choices.find("li.search-choice").remove();
546
+ this.choices = 0;
547
+ } else if (!this.is_multiple) {
548
+ this.selected_item.addClass("chzn-default").find("span").text(this.default_text);
549
+ if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
550
+ this.container.addClass("chzn-container-single-nosearch");
551
+ } else {
552
+ this.container.removeClass("chzn-container-single-nosearch");
553
+ }
554
+ }
555
+ content = '';
556
+ _ref = this.results_data;
557
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
558
+ data = _ref[_i];
559
+ if (data.group) {
560
+ content += this.result_add_group(data);
561
+ } else if (!data.empty) {
562
+ content += this.result_add_option(data);
563
+ if (data.selected && this.is_multiple) {
564
+ this.choice_build(data);
565
+ } else if (data.selected && !this.is_multiple) {
566
+ this.selected_item.removeClass("chzn-default").find("span").text(data.text);
567
+ if (this.allow_single_deselect) {
568
+ this.single_deselect_control_build();
569
+ }
570
+ }
571
+ }
572
+ }
573
+ this.search_field_disabled();
574
+ this.show_search_field_default();
575
+ this.search_field_scale();
576
+ this.search_results.html(content);
577
+ return this.parsing = false;
578
+ };
579
+
580
+ Chosen.prototype.result_add_group = function(group) {
581
+ if (!group.disabled) {
582
+ group.dom_id = this.container_id + "_g_" + group.array_index;
583
+ return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>';
584
+ } else {
585
+ return "";
586
+ }
587
+ };
588
+
589
+ Chosen.prototype.result_do_highlight = function(el) {
590
+ var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
591
+ if (el.length) {
592
+ this.result_clear_highlight();
593
+ this.result_highlight = el;
594
+ this.result_highlight.addClass("highlighted");
595
+ maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
596
+ visible_top = this.search_results.scrollTop();
597
+ visible_bottom = maxHeight + visible_top;
598
+ high_top = this.result_highlight.position().top + this.search_results.scrollTop();
599
+ high_bottom = high_top + this.result_highlight.outerHeight();
600
+ if (high_bottom >= visible_bottom) {
601
+ return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
602
+ } else if (high_top < visible_top) {
603
+ return this.search_results.scrollTop(high_top);
604
+ }
605
+ }
606
+ };
607
+
608
+ Chosen.prototype.result_clear_highlight = function() {
609
+ if (this.result_highlight) {
610
+ this.result_highlight.removeClass("highlighted");
611
+ }
612
+ return this.result_highlight = null;
613
+ };
614
+
615
+ Chosen.prototype.results_show = function() {
616
+ var dd_top;
617
+ if (!this.is_multiple) {
618
+ this.selected_item.addClass("chzn-single-with-drop");
619
+ if (this.result_single_selected) {
620
+ this.result_do_highlight(this.result_single_selected);
621
+ }
622
+ } else if (this.max_selected_options <= this.choices) {
623
+ this.form_field_jq.trigger("liszt:maxselected", {
624
+ chosen: this
625
+ });
626
+ return false;
627
+ }
628
+ dd_top = this.is_multiple ? this.container.height() : this.container.height() - 1;
629
+ this.form_field_jq.trigger("liszt:showing_dropdown", {
630
+ chosen: this
631
+ });
632
+ this.dropdown.css({
633
+ "top": dd_top + "px",
634
+ "left": 0
635
+ });
636
+ this.results_showing = true;
637
+ this.search_field.focus();
638
+ this.search_field.val(this.search_field.val());
639
+ return this.winnow_results();
640
+ };
641
+
642
+ Chosen.prototype.results_hide = function() {
643
+ if (!this.is_multiple) {
644
+ this.selected_item.removeClass("chzn-single-with-drop");
645
+ }
646
+ this.result_clear_highlight();
647
+ this.form_field_jq.trigger("liszt:hiding_dropdown", {
648
+ chosen: this
649
+ });
650
+ this.dropdown.css({
651
+ "left": "-9000px"
652
+ });
653
+ return this.results_showing = false;
654
+ };
655
+
656
+ Chosen.prototype.set_tab_index = function(el) {
657
+ var ti;
658
+ if (this.form_field_jq.attr("tabindex")) {
659
+ ti = this.form_field_jq.attr("tabindex");
660
+ this.form_field_jq.attr("tabindex", -1);
661
+ return this.search_field.attr("tabindex", ti);
662
+ }
663
+ };
664
+
665
+ Chosen.prototype.show_search_field_default = function() {
666
+ if (this.is_multiple && this.choices < 1 && !this.active_field) {
667
+ this.search_field.val(this.default_text);
668
+ return this.search_field.addClass("default");
669
+ } else {
670
+ this.search_field.val("");
671
+ return this.search_field.removeClass("default");
672
+ }
673
+ };
674
+
675
+ Chosen.prototype.search_results_mouseup = function(evt) {
676
+ var target;
677
+ target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
678
+ if (target.length) {
679
+ this.result_highlight = target;
680
+ this.result_select(evt);
681
+ return this.search_field.focus();
682
+ }
683
+ };
684
+
685
+ Chosen.prototype.search_results_mouseover = function(evt) {
686
+ var target;
687
+ target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
688
+ if (target) {
689
+ return this.result_do_highlight(target);
690
+ }
691
+ };
692
+
693
+ Chosen.prototype.search_results_mouseout = function(evt) {
694
+ if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
695
+ return this.result_clear_highlight();
696
+ }
697
+ };
698
+
699
+ Chosen.prototype.choices_click = function(evt) {
700
+ evt.preventDefault();
701
+ if (this.active_field && !($(evt.target).hasClass("search-choice" || $(evt.target).parents('.search-choice').first)) && !this.results_showing) {
702
+ return this.results_show();
703
+ }
704
+ };
705
+
706
+ Chosen.prototype.choice_build = function(item) {
707
+ var choice_id, html, link,
708
+ _this = this;
709
+ if (this.is_multiple && this.max_selected_options <= this.choices) {
710
+ this.form_field_jq.trigger("liszt:maxselected", {
711
+ chosen: this
712
+ });
713
+ return false;
714
+ }
715
+ choice_id = this.container_id + "_c_" + item.array_index;
716
+ this.choices += 1;
717
+ if (item.disabled) {
718
+ html = '<li class="search-choice search-choice-disabled" id="' + choice_id + '"><span>' + item.html + '</span></li>';
719
+ } else {
720
+ html = '<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>';
721
+ }
722
+ this.search_container.before(html);
723
+ link = $('#' + choice_id).find("a").first();
724
+ return link.click(function(evt) {
725
+ return _this.choice_destroy_link_click(evt);
726
+ });
727
+ };
728
+
729
+ Chosen.prototype.choice_destroy_link_click = function(evt) {
730
+ evt.preventDefault();
731
+ if (!this.is_disabled) {
732
+ this.pending_destroy_click = true;
733
+ return this.choice_destroy($(evt.target));
734
+ } else {
735
+ return evt.stopPropagation;
736
+ }
737
+ };
738
+
739
+ Chosen.prototype.choice_destroy = function(link) {
740
+ if (this.result_deselect(link.attr("rel"))) {
741
+ this.choices -= 1;
742
+ this.show_search_field_default();
743
+ if (this.is_multiple && this.choices > 0 && this.search_field.val().length < 1) {
744
+ this.results_hide();
745
+ }
746
+ link.parents('li').first().remove();
747
+ return this.search_field_scale();
748
+ }
749
+ };
750
+
751
+ Chosen.prototype.results_reset = function() {
752
+ this.form_field.options[0].selected = true;
753
+ this.selected_item.find("span").text(this.default_text);
754
+ if (!this.is_multiple) {
755
+ this.selected_item.addClass("chzn-default");
756
+ }
757
+ this.show_search_field_default();
758
+ this.results_reset_cleanup();
759
+ this.form_field_jq.trigger("change");
760
+ if (this.active_field) {
761
+ return this.results_hide();
762
+ }
763
+ };
764
+
765
+ Chosen.prototype.results_reset_cleanup = function() {
766
+ this.current_value = this.form_field_jq.val();
767
+ return this.selected_item.find("abbr").remove();
768
+ };
769
+
770
+ Chosen.prototype.result_select = function(evt) {
771
+ var high, high_id, item, position;
772
+ if (this.result_highlight) {
773
+ high = this.result_highlight;
774
+ high_id = high.attr("id");
775
+ this.result_clear_highlight();
776
+ if (this.is_multiple) {
777
+ this.result_deactivate(high);
778
+ } else {
779
+ this.search_results.find(".result-selected").removeClass("result-selected");
780
+ this.result_single_selected = high;
781
+ this.selected_item.removeClass("chzn-default");
782
+ }
783
+ high.addClass("result-selected");
784
+ position = high_id.substr(high_id.lastIndexOf("_") + 1);
785
+ item = this.results_data[position];
786
+ item.selected = true;
787
+ this.form_field.options[item.options_index].selected = true;
788
+ if (this.is_multiple) {
789
+ this.choice_build(item);
790
+ } else {
791
+ this.selected_item.find("span").first().text(item.text);
792
+ if (this.allow_single_deselect) {
793
+ this.single_deselect_control_build();
794
+ }
795
+ }
796
+ if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) {
797
+ this.results_hide();
798
+ }
799
+ this.search_field.val("");
800
+ if (this.is_multiple || this.form_field_jq.val() !== this.current_value) {
801
+ this.form_field_jq.trigger("change", {
802
+ 'selected': this.form_field.options[item.options_index].value
803
+ });
804
+ }
805
+ this.current_value = this.form_field_jq.val();
806
+ return this.search_field_scale();
807
+ }
808
+ };
809
+
810
+ Chosen.prototype.result_activate = function(el) {
811
+ return el.addClass("active-result");
812
+ };
813
+
814
+ Chosen.prototype.result_deactivate = function(el) {
815
+ return el.removeClass("active-result");
816
+ };
817
+
818
+ Chosen.prototype.result_deselect = function(pos) {
819
+ var result, result_data;
820
+ result_data = this.results_data[pos];
821
+ if (!this.form_field.options[result_data.options_index].disabled) {
822
+ result_data.selected = false;
823
+ this.form_field.options[result_data.options_index].selected = false;
824
+ result = $("#" + this.container_id + "_o_" + pos);
825
+ result.removeClass("result-selected").addClass("active-result").show();
826
+ this.result_clear_highlight();
827
+ this.winnow_results();
828
+ this.form_field_jq.trigger("change", {
829
+ deselected: this.form_field.options[result_data.options_index].value
830
+ });
831
+ this.search_field_scale();
832
+ return true;
833
+ } else {
834
+ return false;
835
+ }
836
+ };
837
+
838
+ Chosen.prototype.single_deselect_control_build = function() {
839
+ if (this.allow_single_deselect && this.selected_item.find("abbr").length < 1) {
840
+ return this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
841
+ }
842
+ };
843
+
844
+ Chosen.prototype.winnow_results = function() {
845
+ var found, option, part, parts, regex, regexAnchor, result, result_id, results, searchText, startpos, text, zregex, _i, _j, _len, _len1, _ref;
846
+ this.no_results_clear();
847
+ results = 0;
848
+ searchText = this.search_field.val() === this.default_text ? "" : $('<div/>').text($.trim(this.search_field.val())).html();
849
+ regexAnchor = this.search_contains ? "" : "^";
850
+ regex = new RegExp(regexAnchor + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
851
+ zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
852
+ _ref = this.results_data;
853
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
854
+ option = _ref[_i];
855
+ if (!option.disabled && !option.empty) {
856
+ if (option.group) {
857
+ $('#' + option.dom_id).css('display', 'none');
858
+ } else if (!(this.is_multiple && option.selected)) {
859
+ found = false;
860
+ result_id = option.dom_id;
861
+ result = $("#" + result_id);
862
+ if (regex.test(option.html)) {
863
+ found = true;
864
+ results += 1;
865
+ } else if (this.enable_split_word_search && (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0)) {
866
+ parts = option.html.replace(/\[|\]/g, "").split(" ");
867
+ if (parts.length) {
868
+ for (_j = 0, _len1 = parts.length; _j < _len1; _j++) {
869
+ part = parts[_j];
870
+ if (regex.test(part)) {
871
+ found = true;
872
+ results += 1;
873
+ }
874
+ }
875
+ }
876
+ }
877
+ if (found) {
878
+ if (searchText.length) {
879
+ startpos = option.html.search(zregex);
880
+ text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length);
881
+ text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
882
+ } else {
883
+ text = option.html;
884
+ }
885
+ result.html(text);
886
+ this.result_activate(result);
887
+ if (option.group_array_index != null) {
888
+ $("#" + this.results_data[option.group_array_index].dom_id).css('display', 'list-item');
889
+ }
890
+ } else {
891
+ if (this.result_highlight && result_id === this.result_highlight.attr('id')) {
892
+ this.result_clear_highlight();
893
+ }
894
+ this.result_deactivate(result);
895
+ }
896
+ }
897
+ }
898
+ }
899
+ if (results < 1 && searchText.length) {
900
+ return this.no_results(searchText);
901
+ } else {
902
+ return this.winnow_results_set_highlight();
903
+ }
904
+ };
905
+
906
+ Chosen.prototype.winnow_results_clear = function() {
907
+ var li, lis, _i, _len, _results;
908
+ this.search_field.val("");
909
+ lis = this.search_results.find("li");
910
+ _results = [];
911
+ for (_i = 0, _len = lis.length; _i < _len; _i++) {
912
+ li = lis[_i];
913
+ li = $(li);
914
+ if (li.hasClass("group-result")) {
915
+ _results.push(li.css('display', 'auto'));
916
+ } else if (!this.is_multiple || !li.hasClass("result-selected")) {
917
+ _results.push(this.result_activate(li));
918
+ } else {
919
+ _results.push(void 0);
920
+ }
921
+ }
922
+ return _results;
923
+ };
924
+
925
+ Chosen.prototype.winnow_results_set_highlight = function() {
926
+ var do_high, selected_results;
927
+ if (!this.result_highlight) {
928
+ selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
929
+ do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
930
+ if (do_high != null) {
931
+ return this.result_do_highlight(do_high);
932
+ }
933
+ }
934
+ };
935
+
936
+ Chosen.prototype.no_results = function(terms) {
937
+ var no_results_html;
938
+ no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>');
939
+ no_results_html.find("span").first().html(terms);
940
+ return this.search_results.append(no_results_html);
941
+ };
942
+
943
+ Chosen.prototype.no_results_clear = function() {
944
+ return this.search_results.find(".no-results").remove();
945
+ };
946
+
947
+ Chosen.prototype.keydown_arrow = function() {
948
+ var first_active, next_sib;
949
+ if (!this.result_highlight) {
950
+ first_active = this.search_results.find("li.active-result").first();
951
+ if (first_active) {
952
+ this.result_do_highlight($(first_active));
953
+ }
954
+ } else if (this.results_showing) {
955
+ next_sib = this.result_highlight.nextAll("li.active-result").first();
956
+ if (next_sib) {
957
+ this.result_do_highlight(next_sib);
958
+ }
959
+ }
960
+ if (!this.results_showing) {
961
+ return this.results_show();
962
+ }
963
+ };
964
+
965
+ Chosen.prototype.keyup_arrow = function() {
966
+ var prev_sibs;
967
+ if (!this.results_showing && !this.is_multiple) {
968
+ return this.results_show();
969
+ } else if (this.result_highlight) {
970
+ prev_sibs = this.result_highlight.prevAll("li.active-result");
971
+ if (prev_sibs.length) {
972
+ return this.result_do_highlight(prev_sibs.first());
973
+ } else {
974
+ if (this.choices > 0) {
975
+ this.results_hide();
976
+ }
977
+ return this.result_clear_highlight();
978
+ }
979
+ }
980
+ };
981
+
982
+ Chosen.prototype.keydown_backstroke = function() {
983
+ var next_available_destroy;
984
+ if (this.pending_backstroke) {
985
+ this.choice_destroy(this.pending_backstroke.find("a").first());
986
+ return this.clear_backstroke();
987
+ } else {
988
+ next_available_destroy = this.search_container.siblings("li.search-choice").last();
989
+ if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
990
+ this.pending_backstroke = next_available_destroy;
991
+ if (this.single_backstroke_delete) {
992
+ return this.keydown_backstroke();
993
+ } else {
994
+ return this.pending_backstroke.addClass("search-choice-focus");
995
+ }
996
+ }
997
+ }
998
+ };
999
+
1000
+ Chosen.prototype.clear_backstroke = function() {
1001
+ if (this.pending_backstroke) {
1002
+ this.pending_backstroke.removeClass("search-choice-focus");
1003
+ }
1004
+ return this.pending_backstroke = null;
1005
+ };
1006
+
1007
+ Chosen.prototype.keydown_checker = function(evt) {
1008
+ var stroke, _ref;
1009
+ stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
1010
+ this.search_field_scale();
1011
+ if (stroke !== 8 && this.pending_backstroke) {
1012
+ this.clear_backstroke();
1013
+ }
1014
+ switch (stroke) {
1015
+ case 8:
1016
+ this.backstroke_length = this.search_field.val().length;
1017
+ break;
1018
+ case 9:
1019
+ if (this.results_showing && !this.is_multiple) {
1020
+ this.result_select(evt);
1021
+ }
1022
+ this.mouse_on_container = false;
1023
+ break;
1024
+ case 13:
1025
+ evt.preventDefault();
1026
+ break;
1027
+ case 38:
1028
+ evt.preventDefault();
1029
+ this.keyup_arrow();
1030
+ break;
1031
+ case 40:
1032
+ this.keydown_arrow();
1033
+ break;
1034
+ }
1035
+ };
1036
+
1037
+ Chosen.prototype.search_field_scale = function() {
1038
+ var dd_top, div, h, style, style_block, styles, w, _i, _len;
1039
+ if (this.is_multiple) {
1040
+ h = 0;
1041
+ w = 0;
1042
+ style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
1043
+ styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
1044
+ for (_i = 0, _len = styles.length; _i < _len; _i++) {
1045
+ style = styles[_i];
1046
+ style_block += style + ":" + this.search_field.css(style) + ";";
1047
+ }
1048
+ div = $('<div />', {
1049
+ 'style': style_block
1050
+ });
1051
+ div.text(this.search_field.val());
1052
+ $('body').append(div);
1053
+ w = div.width() + 25;
1054
+ div.remove();
1055
+ if (w > this.f_width - 10) {
1056
+ w = this.f_width - 10;
1057
+ }
1058
+ this.search_field.css({
1059
+ 'width': w + 'px'
1060
+ });
1061
+ dd_top = this.container.height();
1062
+ return this.dropdown.css({
1063
+ "top": dd_top + "px"
1064
+ });
1065
+ }
1066
+ };
1067
+
1068
+ Chosen.prototype.generate_random_id = function() {
1069
+ var string;
1070
+ string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char();
1071
+ while ($("#" + string).length > 0) {
1072
+ string += this.generate_random_char();
1073
+ }
1074
+ return string;
1075
+ };
1076
+
1077
+ return Chosen;
1078
+
1079
+ })(AbstractChosen);
1080
+
1081
+ root.Chosen = Chosen;
1082
+
1083
+ get_side_border_padding = function(elmt) {
1084
+ var side_border_padding;
1085
+ return side_border_padding = elmt.outerWidth() - elmt.width();
1086
+ };
1087
+
1088
+ root.get_side_border_padding = get_side_border_padding;
1089
+
1090
+ }).call(this);
assets/js/chosen/chosen.jquery.min.js ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ // Chosen, a Select Box Enhancer for jQuery and Protoype
2
+ // by Patrick Filler for Harvest, http://getharvest.com
3
+ //
4
+ // Version 0.9.12
5
+ // Full source at https://github.com/harvesthq/chosen
6
+ // Copyright (c) 2011 Harvest http://getharvest.com
7
+
8
+ // MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
9
+ // This file is generated by `cake build`, do not edit it by hand.
10
+ (function(){var e;e=function(){function e(){this.options_index=0,this.parsed=[]}return e.prototype.add_node=function(e){return e.nodeName.toUpperCase()==="OPTGROUP"?this.add_group(e):this.add_option(e)},e.prototype.add_group=function(e){var t,n,r,i,s,o;t=this.parsed.length,this.parsed.push({array_index:t,group:!0,label:e.label,children:0,disabled:e.disabled}),s=e.childNodes,o=[];for(r=0,i=s.length;r<i;r++)n=s[r],o.push(this.add_option(n,t,e.disabled));return o},e.prototype.add_option=function(e,t,n){if(e.nodeName.toUpperCase()==="OPTION")return e.text!==""?(t!=null&&(this.parsed[t].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:e.value,text:e.text,html:e.innerHTML,selected:e.selected,disabled:n===!0?n:e.disabled,group_array_index:t,classes:e.className,style:e.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1},e}(),e.select_to_array=function(t){var n,r,i,s,o;r=new e,o=t.childNodes;for(i=0,s=o.length;i<s;i++)n=o[i],r.add_node(n);return r.parsed},this.SelectParser=e}).call(this),function(){var e,t;t=this,e=function(){function e(e,t){this.form_field=e,this.options=t!=null?t:{},this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers(),this.finish_setup()}return e.prototype.set_default_values=function(){var e=this;return this.click_test_action=function(t){return e.test_active_click(t)},this.activate_action=function(t){return e.activate_field(t)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null,this.allow_single_deselect=this.options.allow_single_deselect!=null&&this.form_field.options[0]!=null&&this.form_field.options[0].text===""?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=this.options.enable_split_word_search!=null?this.options.enable_split_word_search:!0,this.search_contains=this.options.search_contains||!1,this.choices=0,this.single_backstroke_delete=this.options.single_backstroke_delete||!1,this.max_selected_options=this.options.max_selected_options||Infinity,this.inherit_select_classes=this.options.inherit_select_classes||!1},e.prototype.set_default_text=function(){return this.form_field.getAttribute("data-placeholder")?this.default_text=this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.default_text=this.options.placeholder_text_multiple||this.options.placeholder_text||"Select Some Options":this.default_text=this.options.placeholder_text_single||this.options.placeholder_text||"Select an Option",this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||"No results match"},e.prototype.mouse_enter=function(){return this.mouse_on_container=!0},e.prototype.mouse_leave=function(){return this.mouse_on_container=!1},e.prototype.input_focus=function(e){var t=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return t.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},e.prototype.input_blur=function(e){var t=this;if(!this.mouse_on_container)return this.active_field=!1,setTimeout(function(){return t.blur_test()},100)},e.prototype.result_add_option=function(e){var t,n;return e.disabled?"":(e.dom_id=this.container_id+"_o_"+e.array_index,t=e.selected&&this.is_multiple?[]:["active-result"],e.selected&&t.push("result-selected"),e.group_array_index!=null&&t.push("group-option"),e.classes!==""&&t.push(e.classes),n=e.style.cssText!==""?' style="'+e.style+'"':"",'<li id="'+e.dom_id+'" class="'+t.join(" ")+'"'+n+">"+e.html+"</li>")},e.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.result_single_selected=null,this.results_build()},e.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},e.prototype.results_search=function(e){return this.results_showing?this.winnow_results():this.results_show()},e.prototype.keyup_checker=function(e){var t,n;t=(n=e.which)!=null?n:e.keyCode,this.search_field_scale();switch(t){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:e.preventDefault();if(this.results_showing)return this.result_select(e);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},e.prototype.generate_field_id=function(){var e;return e=this.generate_random_id(),this.form_field.id=e,e},e.prototype.generate_random_char=function(){var e,t,n;return e="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",n=Math.floor(Math.random()*e.length),t=e.substring(n,n+1)},e}(),t.AbstractChosen=e}.call(this),function(){var e,t,n,r,i={}.hasOwnProperty,s=function(e,t){function r(){this.constructor=e}for(var n in t)i.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r,e.__super__=t.prototype,e};r=this,e=jQuery,e.fn.extend({chosen:function(n){var r,i,s;return s=navigator.userAgent.toLowerCase(),i=/(msie) ([\w.]+)/.exec(s)||[],r={name:i[1]||"",version:i[2]||"0"},r.name==="msie"&&(r.version==="6.0"||r.version==="7.0"&&document.documentMode===7)?this:this.each(function(r){var i;i=e(this);if(!i.hasClass("chzn-done"))return i.data("chosen",new t(this,n))})}}),t=function(t){function i(){return i.__super__.constructor.apply(this,arguments)}return s(i,t),i.prototype.setup=function(){return this.form_field_jq=e(this.form_field),this.current_value=this.form_field_jq.val(),this.is_rtl=this.form_field_jq.hasClass("chzn-rtl")},i.prototype.finish_setup=function(){return this.form_field_jq.addClass("chzn-done")},i.prototype.set_up_html=function(){var t,r,i,s,o,u;return this.container_id=this.form_field.id.length?this.form_field.id.replace(/[^\w]/g,"_"):this.generate_field_id(),this.container_id+="_chzn",t=["chzn-container"],t.push("chzn-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&t.push(this.form_field.className),this.is_rtl&&t.push("chzn-rtl"),this.f_width=this.form_field_jq.outerWidth(),i={id:this.container_id,"class":t.join(" "),style:"width: "+this.f_width+"px;",title:this.form_field.title},r=e("<div />",i),this.is_multiple?r.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'):r.html('<a href="javascript:void(0)" class="chzn-single chzn-default" tabindex="-1"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'),this.form_field_jq.hide().after(r),this.container=e("#"+this.container_id),this.dropdown=this.container.find("div.chzn-drop").first(),s=this.container.height(),o=this.f_width-n(this.dropdown),this.dropdown.css({width:o+"px",top:s+"px"}),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chzn-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chzn-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chzn-search").first(),this.selected_item=this.container.find(".chzn-single").first(),u=o-n(this.search_container)-n(this.search_field),this.search_field.css({width:u+"px"})),this.results_build(),this.set_tab_index(),this.form_field_jq.trigger("liszt:ready",{chosen:this})},i.prototype.register_observers=function(){var e=this;return this.container.mousedown(function(t){e.container_mousedown(t)}),this.container.mouseup(function(t){e.container_mouseup(t)}),this.container.mouseenter(function(t){e.mouse_enter(t)}),this.container.mouseleave(function(t){e.mouse_leave(t)}),this.search_results.mouseup(function(t){e.search_results_mouseup(t)}),this.search_results.mouseover(function(t){e.search_results_mouseover(t)}),this.search_results.mouseout(function(t){e.search_results_mouseout(t)}),this.form_field_jq.bind("liszt:updated",function(t){e.results_update_field(t)}),this.form_field_jq.bind("liszt:activate",function(t){e.activate_field(t)}),this.form_field_jq.bind("liszt:open",function(t){e.container_mousedown(t)}),this.search_field.blur(function(t){e.input_blur(t)}),this.search_field.keyup(function(t){e.keyup_checker(t)}),this.search_field.keydown(function(t){e.keydown_checker(t)}),this.search_field.focus(function(t){e.input_focus(t)}),this.is_multiple?this.search_choices.click(function(t){e.choices_click(t)}):this.container.click(function(e){e.preventDefault()})},i.prototype.search_field_disabled=function(){this.is_disabled=this.form_field_jq[0].disabled;if(this.is_disabled)return this.container.addClass("chzn-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus",this.activate_action),this.close_field();this.container.removeClass("chzn-disabled"),this.search_field[0].disabled=!1;if(!this.is_multiple)return this.selected_item.bind("focus",this.activate_action)},i.prototype.container_mousedown=function(t){var n;if(!this.is_disabled)return n=t!=null?e(t.target).hasClass("search-choice-close"):!1,t&&t.type==="mousedown"&&!this.results_showing&&t.preventDefault(),!this.pending_destroy_click&&!n?(this.active_field?!this.is_multiple&&t&&(e(t.target)[0]===this.selected_item[0]||e(t.target).parents("a.chzn-single").length)&&(t.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),e(document).click(this.click_test_action),this.results_show()),this.activate_field()):this.pending_destroy_click=!1},i.prototype.container_mouseup=function(e){if(e.target.nodeName==="ABBR"&&!this.is_disabled)return this.results_reset(e)},i.prototype.blur_test=function(e){if(!this.active_field&&this.container.hasClass("chzn-container-active"))return this.close_field()},i.prototype.close_field=function(){return e(document).unbind("click",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},i.prototype.activate_field=function(){return this.container.addClass("chzn-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},i.prototype.test_active_click=function(t){return e(t.target).parents("#"+this.container_id).length?this.active_field=!0:this.close_field()},i.prototype.results_build=function(){var e,t,n,i,s;this.parsing=!0,this.results_data=r.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.find("li.search-choice").remove(),this.choices=0):this.is_multiple||(this.selected_item.addClass("chzn-default").find("span").text(this.default_text),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?this.container.addClass("chzn-container-single-nosearch"):this.container.removeClass("chzn-container-single-nosearch")),e="",s=this.results_data;for(n=0,i=s.length;n<i;n++)t=s[n],t.group?e+=this.result_add_group(t):t.empty||(e+=this.result_add_option(t),t.selected&&this.is_multiple?this.choice_build(t):t.selected&&!this.is_multiple&&(this.selected_item.removeClass("chzn-default").find("span").text(t.text),this.allow_single_deselect&&this.single_deselect_control_build()));return this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.search_results.html(e),this.parsing=!1},i.prototype.result_add_group=function(t){return t.disabled?"":(t.dom_id=this.container_id+"_g_"+t.array_index,'<li id="'+t.dom_id+'" class="group-result">'+e("<div />").text(t.label).html()+"</li>")},i.prototype.result_do_highlight=function(e){var t,n,r,i,s;if(e.length){this.result_clear_highlight(),this.result_highlight=e,this.result_highlight.addClass("highlighted"),r=parseInt(this.search_results.css("maxHeight"),10),s=this.search_results.scrollTop(),i=r+s,n=this.result_highlight.position().top+this.search_results.scrollTop(),t=n+this.result_highlight.outerHeight();if(t>=i)return this.search_results.scrollTop(t-r>0?t-r:0);if(n<s)return this.search_results.scrollTop(n)}},i.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},i.prototype.results_show=function(){var e;if(!this.is_multiple)this.selected_item.addClass("chzn-single-with-drop"),this.result_single_selected&&this.result_do_highlight(this.result_single_selected);else if(this.max_selected_options<=this.choices)return this.form_field_jq.trigger("liszt:maxselected",{chosen:this}),!1;return e=this.is_multiple?this.container.height():this.container.height()-1,this.form_field_jq.trigger("liszt:showing_dropdown",{chosen:this}),this.dropdown.css({top:e+"px",left:0}),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results()},i.prototype.results_hide=function(){return this.is_multiple||this.selected_item.removeClass("chzn-single-with-drop"),this.result_clear_highlight(),this.form_field_jq.trigger("liszt:hiding_dropdown",{chosen:this}),this.dropdown.css({left:"-9000px"}),this.results_showing=!1},i.prototype.set_tab_index=function(e){var t;if(this.form_field_jq.attr("tabindex"))return t=this.form_field_jq.attr("tabindex"),this.form_field_jq.attr("tabindex",-1),this.search_field.attr("tabindex",t)},i.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},i.prototype.search_results_mouseup=function(t){var n;n=e(t.target).hasClass("active-result")?e(t.target):e(t.target).parents(".active-result").first();if(n.length)return this.result_highlight=n,this.result_select(t),this.search_field.focus()},i.prototype.search_results_mouseover=function(t){var n;n=e(t.target).hasClass("active-result")?e(t.target):e(t.target).parents(".active-result").first();if(n)return this.result_do_highlight(n)},i.prototype.search_results_mouseout=function(t){if(e(t.target).hasClass("active-result"))return this.result_clear_highlight()},i.prototype.choices_click=function(t){t.preventDefault();if(this.active_field&&!e(t.target).hasClass("search-choice")&&!this.results_showing)return this.results_show()},i.prototype.choice_build=function(t){var n,r,i,s=this;return this.is_multiple&&this.max_selected_options<=this.choices?(this.form_field_jq.trigger("liszt:maxselected",{chosen:this}),!1):(n=this.container_id+"_c_"+t.array_index,this.choices+=1,t.disabled?r='<li class="search-choice search-choice-disabled" id="'+n+'"><span>'+t.html+"</span></li>":r='<li class="search-choice" id="'+n+'"><span>'+t.html+'</span><a href="javascript:void(0)" class="search-choice-close" rel="'+t.array_index+'"></a></li>',this.search_container.before(r),i=e("#"+n).find("a").first(),i.click(function(e){return s.choice_destroy_link_click(e)}))},i.prototype.choice_destroy_link_click=function(t){return t.preventDefault(),this.is_disabled?t.stopPropagation:(this.pending_destroy_click=!0,this.choice_destroy(e(t.target)))},i.prototype.choice_destroy=function(e){if(this.result_deselect(e.attr("rel")))return this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.val().length<1&&this.results_hide(),e.parents("li").first().remove(),this.search_field_scale()},i.prototype.results_reset=function(){this.form_field.options[0].selected=!0,this.selected_item.find("span").text(this.default_text),this.is_multiple||this.selected_item.addClass("chzn-default"),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change");if(this.active_field)return this.results_hide()},i.prototype.results_reset_cleanup=function(){return this.current_value=this.form_field_jq.val(),this.selected_item.find("abbr").remove()},i.prototype.result_select=function(e){var t,n,r,i;if(this.result_highlight)return t=this.result_highlight,n=t.attr("id"),this.result_clear_highlight(),this.is_multiple?this.result_deactivate(t):(this.search_results.find(".result-selected").removeClass("result-selected"),this.result_single_selected=t,this.selected_item.removeClass("chzn-default")),t.addClass("result-selected"),i=n.substr(n.lastIndexOf("_")+1),r=this.results_data[i],r.selected=!0,this.form_field.options[r.options_index].selected=!0,this.is_multiple?this.choice_build(r):(this.selected_item.find("span").first().text(r.text),this.allow_single_deselect&&this.single_deselect_control_build()),(!e.metaKey&&!e.ctrlKey||!this.is_multiple)&&this.results_hide(),this.search_field.val(""),(this.is_multiple||this.form_field_jq.val()!==this.current_value)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[r.options_index].value}),this.current_value=this.form_field_jq.val(),this.search_field_scale()},i.prototype.result_activate=function(e){return e.addClass("active-result")},i.prototype.result_deactivate=function(e){return e.removeClass("active-result")},i.prototype.result_deselect=function(t){var n,r;return r=this.results_data[t],this.form_field.options[r.options_index].disabled?!1:(r.selected=!1,this.form_field.options[r.options_index].selected=!1,n=e("#"+this.container_id+"_o_"+t),n.removeClass("result-selected").addClass("active-result").show(),this.result_clear_highlight(),this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[r.options_index].value}),this.search_field_scale(),!0)},i.prototype.single_deselect_control_build=function(){if(this.allow_single_deselect&&this.selected_item.find("abbr").length<1)return this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>')},i.prototype.winnow_results=function(){var t,n,r,i,s,o,u,a,f,l,c,h,p,d,v,m,g,y;this.no_results_clear(),f=0,l=this.search_field.val()===this.default_text?"":e("<div/>").text(e.trim(this.search_field.val())).html(),o=this.search_contains?"":"^",s=new RegExp(o+l.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),p=new RegExp(l.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),y=this.results_data;for(d=0,m=y.length;d<m;d++){n=y[d];if(!n.disabled&&!n.empty)if(n.group)e("#"+n.dom_id).css("display","none");else if(!this.is_multiple||!n.selected){t=!1,a=n.dom_id,u=e("#"+a);if(s.test(n.html))t=!0,f+=1;else if(this.enable_split_word_search&&(n.html.indexOf(" ")>=0||n.html.indexOf("[")===0)){i=n.html.replace(/\[|\]/g,"").split(" ");if(i.length)for(v=0,g=i.length;v<g;v++)r=i[v],s.test(r)&&(t=!0,f+=1)}t?(l.length?(c=n.html.search(p),h=n.html.substr(0,c+l.length)+"</em>"+n.html.substr(c+l.length),h=h.substr(0,c)+"<em>"+h.substr(c)):h=n.html,u.html(h),this.result_activate(u),n.group_array_index!=null&&e("#"+this.results_data[n.group_array_index].dom_id).css("display","list-item")):(this.result_highlight&&a===this.result_highlight.attr("id")&&this.result_clear_highlight(),this.result_deactivate(u))}}return f<1&&l.length?this.no_results(l):this.winnow_results_set_highlight()},i.prototype.winnow_results_clear=function(){var t,n,r,i,s;this.search_field.val(""),n=this.search_results.find("li"),s=[];for(r=0,i=n.length;r<i;r++)t=n[r],t=e(t),t.hasClass("group-result")?s.push(t.css("display","auto")):!this.is_multiple||!t.hasClass("result-selected")?s.push(this.result_activate(t)):s.push(void 0);return s},i.prototype.winnow_results_set_highlight=function(){var e,t;if(!this.result_highlight){t=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),e=t.length?t.first():this.search_results.find(".active-result").first();if(e!=null)return this.result_do_highlight(e)}},i.prototype.no_results=function(t){var n;return n=e('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),n.find("span").first().html(t),this.search_results.append(n)},i.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},i.prototype.keydown_arrow=function(){var t,n;this.result_highlight?this.results_showing&&(n=this.result_highlight.nextAll("li.active-result").first(),n&&this.result_do_highlight(n)):(t=this.search_results.find("li.active-result").first(),t&&this.result_do_highlight(e(t)));if(!this.results_showing)return this.results_show()},i.prototype.keyup_arrow=function(){var e;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight)return e=this.result_highlight.prevAll("li.active-result"),e.length?this.result_do_highlight(e.first()):(this.choices>0&&this.results_hide(),this.result_clear_highlight())},i.prototype.keydown_backstroke=function(){var e;if(this.pending_backstroke)return this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke();e=this.search_container.siblings("li.search-choice").last();if(e.length&&!e.hasClass("search-choice-disabled"))return this.pending_backstroke=e,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")},i.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},i.prototype.keydown_checker=function(e){var t,n;t=(n=e.which)!=null?n:e.keyCode,this.search_field_scale(),t!==8&&this.pending_backstroke&&this.clear_backstroke();switch(t){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(e),this.mouse_on_container=!1;break;case 13:e.preventDefault();break;case 38:e.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},i.prototype.search_field_scale=function(){var t,n,r,i,s,o,u,a,f;if(this.is_multiple){r=0,u=0,s="position:absolute; left: -1000px; top: -1000px; display:none;",o=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(a=0,f=o.length;a<f;a++)i=o[a],s+=i+":"+this.search_field.css(i)+";";return n=e("<div />",{style:s}),n.text(this.search_field.val()),e("body").append(n),u=n.width()+25,n.remove(),u>this.f_width-10&&(u=this.f_width-10),this.search_field.css({width:u+"px"}),t=this.container.height(),this.dropdown.css({top:t+"px"})}},i.prototype.generate_random_id=function(){var t;t="sel"+this.generate_random_char()+this.generate_random_char()+this.generate_random_char();while(e("#"+t).length>0)t+=this.generate_random_char();return t},i}(AbstractChosen),r.Chosen=t,n=function(e){var t;return t=e.outerWidth()-e.width()},r.get_side_border_padding=n}.call(this);
assets/js/jqueryFileTree/connectors/jqueryFileTree.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // jQuery File Tree PHP Connector - modified for php4/wordpress compatibility
4
+ //
5
+
6
+ if(file_exists('../../../../../../wp-load.php')) {
7
+ require_once("../../../../../../wp-load.php");
8
+ } else if(file_exists('../../../../../../../wp-load.php')) {
9
+ require_once("../../../../../../../wp-load.php");
10
+ } else {
11
+ if(file_exists('../../../../../../wp-config.php')) {
12
+ require_once("../../../../../../wp-config.php");
13
+ } else if(file_exists('../../../../../../../wp-config.php')) {
14
+ require_once("../../../../../../../wp-config.php");
15
+ } else {
16
+ exit;
17
+ }
18
+ }
19
+
20
+ if (!function_exists('php4_scandir') && !function_exists('scandir')) {
21
+ function php4_scandir($dir,$listDirectories=true) {
22
+ $dirArray = array();
23
+ if ($handle = opendir($dir)) {
24
+ while (false !== ($file = readdir($handle))) {
25
+ if($listDirectories == false) { if(is_dir($file)) { continue; } }
26
+ array_push($dirArray,basename($file));
27
+ }
28
+ closedir($handle);
29
+ }
30
+ return $dirArray;
31
+ }
32
+ }
33
+
34
+ require_once(ABSPATH.'wp-admin/admin.php');
35
+
36
+ $_POST['dir'] = urldecode($_POST['dir']);
37
+
38
+ $root_dir = '';
39
+
40
+ if( file_exists($root_dir . $_POST['dir']) ) {
41
+ if (function_exists('scandir')) {
42
+ $files = scandir($root_dir . $_POST['dir']);
43
+ } else {
44
+ $files = php4_scandir($root_dir . $_POST['dir']);
45
+ }
46
+ natcasesort($files);
47
+ if( count($files) > 2 ) {
48
+ echo "<ul class=\"jqueryFileTree\" style=\"display: none;\">";
49
+ // All dirs
50
+ foreach( $files as $file ) {
51
+ if( file_exists($root_dir . $_POST['dir'] . $file) && $file != '.' && $file != '..' && is_dir($root_dir . $_POST['dir'] . $file) ) {
52
+ echo "<li class=\"directory collapsed\"><a href=\"#\" rel=\"" . htmlentities($_POST['dir'] . $file) . "/\">" . htmlentities($file) . "</a></li>";
53
+ }
54
+ }
55
+ // All files
56
+ foreach( $files as $file ) {
57
+ if( file_exists($root_dir . $_POST['dir'] . $file) && $file != '.' && $file != '..' && !is_dir($root_dir . $_POST['dir'] . $file) ) {
58
+ $ext = preg_replace('/^.*\./', '', $file);
59
+ echo "<li class=\"file ext_$ext\"><a href=\"#\" rel=\"" . htmlentities($_POST['dir'] . $file) . "\">" . htmlentities($file) . "</a></li>";
60
+ }
61
+ }
62
+ echo "</ul>";
63
+ }
64
+ }
65
+
66
+ ?>
assets/js/jqueryFileTree/connectors/jqueryFileTreeDir.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // jQuery File Tree PHP Connector - modified for php4/wordpress compatibility
4
+ //
5
+
6
+ if(file_exists('../../../../../../wp-load.php')) {
7
+ require_once("../../../../../../wp-load.php");
8
+ } else if(file_exists('../../../../../../../wp-load.php')) {
9
+ require_once("../../../../../../../wp-load.php");
10
+ } else {
11
+ if(file_exists('../../../../../../wp-config.php')) {
12
+ require_once("../../../../../../wp-config.php");
13
+ } else if(file_exists('../../../../../../../wp-config.php')) {
14
+ require_once("../../../../../../../wp-config.php");
15
+ } else {
16
+ exit;
17
+ }
18
+ }
19
+
20
+ if (!function_exists('php4_scandir') && !function_exists('scandir')) {
21
+ function php4_scandir($dir,$listDirectories=true) {
22
+ $dirArray = array();
23
+ if ($handle = opendir($dir)) {
24
+ while (false !== ($file = readdir($handle))) {
25
+ if($listDirectories == false) { if(is_dir($file)) { continue; } }
26
+ array_push($dirArray,basename($file));
27
+ }
28
+ closedir($handle);
29
+ }
30
+ return $dirArray;
31
+ }
32
+ }
33
+
34
+ require_once(ABSPATH.'wp-admin/admin.php');
35
+
36
+ $_POST['dir'] = urldecode($_POST['dir']);
37
+
38
+ $root_dir = '';
39
+
40
+ if( file_exists($root_dir . $_POST['dir']) ) {
41
+ if (function_exists('scandir')) {
42
+ $files = scandir($root_dir . $_POST['dir']);
43
+ } else {
44
+ $files = php4_scandir($root_dir . $_POST['dir']);
45
+ }
46
+ natcasesort($files);
47
+ if( count($files) > 2 ) {
48
+ echo "<ul class=\"jqueryFileTree\" style=\"display: none;\">";
49
+ echo "<li class=\"file\"><a href=\"#\" rel=\"" . htmlentities($_POST['dir']) . "\">&lt;Select this directory&gt;</a></li>";
50
+ // All dirs
51
+ foreach( $files as $file ) {
52
+ if( file_exists($root_dir . $_POST['dir'] . $file) && $file != '.' && $file != '..' && is_dir($root_dir . $_POST['dir'] . $file) ) {
53
+ echo "<li class=\"directory collapsed\"><a href=\"#\" rel=\"" . htmlentities($_POST['dir'] . $file) . "/\">" . htmlentities($file) . "</a></li>";
54
+ }
55
+ }
56
+ echo "</ul>";
57
+ }
58
+ }
59
+
60
+ ?>
assets/js/jqueryFileTree/images/application.png ADDED
Binary file
assets/js/jqueryFileTree/images/code.png ADDED
Binary file
assets/js/jqueryFileTree/images/css.png ADDED
Binary file
assets/js/jqueryFileTree/images/db.png ADDED
Binary file
assets/js/jqueryFileTree/images/directory.png ADDED
Binary file
assets/js/jqueryFileTree/images/doc.png ADDED
Binary file
assets/js/jqueryFileTree/images/file.png ADDED
Binary file
assets/js/jqueryFileTree/images/film.png ADDED
Binary file
assets/js/jqueryFileTree/images/flash.png ADDED
Binary file
assets/js/jqueryFileTree/images/folder_open.png ADDED
Binary file
assets/js/jqueryFileTree/images/html.png ADDED
Binary file
assets/js/jqueryFileTree/images/java.png ADDED
Binary file
assets/js/jqueryFileTree/images/linux.png ADDED
Binary file
assets/js/jqueryFileTree/images/music.png ADDED
Binary file
assets/js/jqueryFileTree/images/pdf.png ADDED
Binary file
assets/js/jqueryFileTree/images/php.png ADDED
Binary file
assets/js/jqueryFileTree/images/picture.png ADDED
Binary file
assets/js/jqueryFileTree/images/ppt.png ADDED
Binary file
assets/js/jqueryFileTree/images/psd.png ADDED
Binary file
assets/js/jqueryFileTree/images/ruby.png ADDED
Binary file
assets/js/jqueryFileTree/images/script.png ADDED
Binary file
assets/js/jqueryFileTree/images/spinner.gif ADDED
Binary file
assets/js/jqueryFileTree/images/txt.png ADDED
Binary file
assets/js/jqueryFileTree/images/xls.png ADDED
Binary file
assets/js/jqueryFileTree/images/zip.png ADDED
Binary file
assets/js/jqueryFileTree/jqueryFileTree.css ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #file_browser, #file_browser2, #file_browser_thumbnail {
2
+ border: 1px solid #DFDFDF;
3
+ height: 250px;
4
+ overflow: auto;
5
+ padding: 4px 8px;
6
+ background: #fff;
7
+ margin: 4px 0;
8
+ width: 342px;
9
+ }
10
+
11
+ UL.jqueryFileTree {
12
+ font-family: Verdana, sans-serif;
13
+ font-size: 11px;
14
+ line-height: 18px;
15
+ padding: 0px;
16
+ margin: 0px;
17
+ }
18
+
19
+ UL.jqueryFileTree LI {
20
+ list-style: none;
21
+ padding: 2px 0px;
22
+ padding-left: 20px;
23
+ margin: 0px;
24
+ white-space: nowrap;
25
+ }
26
+
27
+ UL.jqueryFileTree A {
28
+ color: #333;
29
+ text-decoration: none;
30
+ display: block;
31
+ padding: 0px 2px;
32
+ }
33
+
34
+ UL.jqueryFileTree A:hover {
35
+ background: #BDF;
36
+ }
37
+
38
+ /* Core Styles */
39
+ .jqueryFileTree LI.directory { background: url(images/directory.png) left top no-repeat; }
40
+ .jqueryFileTree LI.expanded { background: url(images/folder_open.png) left top no-repeat; }
41
+ .jqueryFileTree LI.file { background: url(images/file.png) left top no-repeat; }
42
+ .jqueryFileTree LI.wait { background: url(images/spinner.gif) left top no-repeat; }
43
+ /* File Extensions*/
44
+ .jqueryFileTree LI.ext_3gp { background: url(images/film.png) left top no-repeat; }
45
+ .jqueryFileTree LI.ext_afp { background: url(images/code.png) left top no-repeat; }
46
+ .jqueryFileTree LI.ext_afpa { background: url(images/code.png) left top no-repeat; }
47
+ .jqueryFileTree LI.ext_asp { background: url(images/code.png) left top no-repeat; }
48
+ .jqueryFileTree LI.ext_aspx { background: url(images/code.png) left top no-repeat; }
49
+ .jqueryFileTree LI.ext_avi { background: url(images/film.png) left top no-repeat; }
50
+ .jqueryFileTree LI.ext_bat { background: url(images/application.png) left top no-repeat; }
51
+ .jqueryFileTree LI.ext_bmp { background: url(images/picture.png) left top no-repeat; }
52
+ .jqueryFileTree LI.ext_c { background: url(images/code.png) left top no-repeat; }
53
+ .jqueryFileTree LI.ext_cfm { background: url(images/code.png) left top no-repeat; }
54
+ .jqueryFileTree LI.ext_cgi { background: url(images/code.png) left top no-repeat; }
55
+ .jqueryFileTree LI.ext_com { background: url(images/application.png) left top no-repeat; }
56
+ .jqueryFileTree LI.ext_cpp { background: url(images/code.png) left top no-repeat; }
57
+ .jqueryFileTree LI.ext_css { background: url(images/css.png) left top no-repeat; }
58
+ .jqueryFileTree LI.ext_doc { background: url(images/doc.png) left top no-repeat; }
59
+ .jqueryFileTree LI.ext_exe { background: url(images/application.png) left top no-repeat; }
60
+ .jqueryFileTree LI.ext_gif { background: url(images/picture.png) left top no-repeat; }
61
+ .jqueryFileTree LI.ext_fla { background: url(images/flash.png) left top no-repeat; }
62
+ .jqueryFileTree LI.ext_h { background: url(images/code.png) left top no-repeat; }
63
+ .jqueryFileTree LI.ext_htm { background: url(images/html.png) left top no-repeat; }
64
+ .jqueryFileTree LI.ext_html { background: url(images/html.png) left top no-repeat; }
65
+ .jqueryFileTree LI.ext_jar { background: url(images/java.png) left top no-repeat; }
66
+ .jqueryFileTree LI.ext_jpg { background: url(images/picture.png) left top no-repeat; }
67
+ .jqueryFileTree LI.ext_jpeg { background: url(images/picture.png) left top no-repeat; }
68
+ .jqueryFileTree LI.ext_js { background: url(images/script.png) left top no-repeat; }
69
+ .jqueryFileTree LI.ext_lasso { background: url(images/code.png) left top no-repeat; }
70
+ .jqueryFileTree LI.ext_log { background: url(images/txt.png) left top no-repeat; }
71
+ .jqueryFileTree LI.ext_m4p { background: url(images/music.png) left top no-repeat; }
72
+ .jqueryFileTree LI.ext_mov { background: url(images/film.png) left top no-repeat; }
73
+ .jqueryFileTree LI.ext_mp3 { background: url(images/music.png) left top no-repeat; }
74
+ .jqueryFileTree LI.ext_mp4 { background: url(images/film.png) left top no-repeat; }
75
+ .jqueryFileTree LI.ext_mpg { background: url(images/film.png) left top no-repeat; }
76
+ .jqueryFileTree LI.ext_mpeg { background: url(images/film.png) left top no-repeat; }
77
+ .jqueryFileTree LI.ext_ogg { background: url(images/music.png) left top no-repeat; }
78
+ .jqueryFileTree LI.ext_pcx { background: url(images/picture.png) left top no-repeat; }
79
+ .jqueryFileTree LI.ext_pdf { background: url(images/pdf.png) left top no-repeat; }
80
+ .jqueryFileTree LI.ext_php { background: url(images/php.png) left top no-repeat; }
81
+ .jqueryFileTree LI.ext_png { background: url(images/picture.png) left top no-repeat; }
82
+ .jqueryFileTree LI.ext_ppt { background: url(images/ppt.png) left top no-repeat; }
83
+ .jqueryFileTree LI.ext_psd { background: url(images/psd.png) left top no-repeat; }
84
+ .jqueryFileTree LI.ext_pl { background: url(images/script.png) left top no-repeat; }
85
+ .jqueryFileTree LI.ext_py { background: url(images/script.png) left top no-repeat; }
86
+ .jqueryFileTree LI.ext_rb { background: url(images/ruby.png) left top no-repeat; }
87
+ .jqueryFileTree LI.ext_rbx { background: url(images/ruby.png) left top no-repeat; }
88
+ .jqueryFileTree LI.ext_rhtml { background: url(images/ruby.png) left top no-repeat; }
89
+ .jqueryFileTree LI.ext_rpm { background: url(images/linux.png) left top no-repeat; }
90
+ .jqueryFileTree LI.ext_ruby { background: url(images/ruby.png) left top no-repeat; }
91
+ .jqueryFileTree LI.ext_sql { background: url(images/db.png) left top no-repeat; }
92
+ .jqueryFileTree LI.ext_swf { background: url(images/flash.png) left top no-repeat; }
93
+ .jqueryFileTree LI.ext_tif { background: url(images/picture.png) left top no-repeat; }
94
+ .jqueryFileTree LI.ext_tiff { background: url(images/picture.png) left top no-repeat; }
95
+ .jqueryFileTree LI.ext_txt { background: url(images/txt.png) left top no-repeat; }
96
+ .jqueryFileTree LI.ext_vb { background: url(images/code.png) left top no-repeat; }
97
+ .jqueryFileTree LI.ext_wav { background: url(images/music.png) left top no-repeat; }
98
+ .jqueryFileTree LI.ext_wmv { background: url(images/film.png) left top no-repeat; }
99
+ .jqueryFileTree LI.ext_xls { background: url(images/xls.png) left top no-repeat; }
100
+ .jqueryFileTree LI.ext_xml { background: url(images/code.png) left top no-repeat; }
101
+ .jqueryFileTree LI.ext_zip { background: url(images/zip.png) left top no-repeat; }
assets/js/jqueryFileTree/jqueryFileTree.js ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jQuery File Tree Plugin
2
+ //
3
+ // Version 1.01
4
+ //
5
+ // Cory S.N. LaViska
6
+ // A Beautiful Site (http://abeautifulsite.net/)
7
+ // 24 March 2008
8
+ //
9
+ // Visit http://abeautifulsite.net/notebook.php?article=58 for more information
10
+ //
11
+ // Usage: $('.fileTreeDemo').fileTree( options, callback )
12
+ //
13
+ // Options: root - root folder to display; default = /
14
+ // script - location of the serverside AJAX file to use; default = jqueryFileTree.php
15
+ // folderEvent - event to trigger expand/collapse; default = click
16
+ // expandSpeed - default = 500 (ms); use -1 for no animation
17
+ // collapseSpeed - default = 500 (ms); use -1 for no animation
18
+ // expandEasing - easing function to use on expand (optional)
19
+ // collapseEasing - easing function to use on collapse (optional)
20
+ // multiFolder - whether or not to limit the browser to one subfolder at a time
21
+ // loadMessage - Message to display while initial tree loads (can be HTML)
22
+ //
23
+ // History:
24
+ //
25
+ // 1.01 - updated to work with foreign characters in directory/file names (12 April 2008)
26
+ // 1.00 - released (24 March 2008)
27
+ //
28
+ // TERMS OF USE
29
+ //
30
+ // This plugin is dual-licensed under the GNU General Public License and the MIT License and
31
+ // is copyright 2008 A Beautiful Site, LLC.
32
+ //
33
+ if(jQuery) (function($){
34
+
35
+ $.extend($.fn, {
36
+ fileTree: function(o, h) {
37
+ // Defaults
38
+ if( !o ) var o = {};
39
+ if( o.root == undefined ) o.root = '/';
40
+ if( o.script == undefined ) o.script = 'jqueryFileTree.php';
41
+ if( o.folderEvent == undefined ) o.folderEvent = 'click';
42
+ if( o.expandSpeed == undefined ) o.expandSpeed= 500;
43
+ if( o.collapseSpeed == undefined ) o.collapseSpeed= 500;
44
+ if( o.expandEasing == undefined ) o.expandEasing = null;
45
+ if( o.collapseEasing == undefined ) o.collapseEasing = null;
46
+ if( o.multiFolder == undefined ) o.multiFolder = true;
47
+ if( o.loadMessage == undefined ) o.loadMessage = 'Loading...';
48
+
49
+ $(this).each( function() {
50
+
51
+ function showTree(c, t) {
52
+ $(c).addClass('wait');
53
+ $(".jqueryFileTree.start").remove();
54
+ $.post(o.script, { dir: t }, function(data) {
55
+ $(c).find('.start').html('');
56
+ $(c).removeClass('wait').append(data);
57
+ if( o.root == t ) $(c).find('UL:hidden').show(); else $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing });
58
+ bindTree(c);
59
+ });
60
+ }
61
+
62
+ function bindTree(t) {
63
+ $(t).find('LI A').bind(o.folderEvent, function() {
64
+ if( $(this).parent().hasClass('directory') ) {
65
+ if( $(this).parent().hasClass('collapsed') ) {
66
+ // Expand
67
+ if( !o.multiFolder ) {
68
+ $(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
69
+ $(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed');
70
+ }
71
+ $(this).parent().find('UL').remove(); // cleanup
72
+ showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) );
73
+ $(this).parent().removeClass('collapsed').addClass('expanded');
74
+ } else {
75
+ // Collapse
76
+ $(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing });
77
+ $(this).parent().removeClass('expanded').addClass('collapsed');
78
+ }
79
+ } else {
80
+ h($(this).attr('rel'));
81
+ }
82
+ return false;
83
+ });
84
+ // Prevent A from triggering the # on non-click events
85
+ if( o.folderEvent.toLowerCase != 'click' ) $(t).find('LI A').bind('click', function() { return false; });
86
+ }
87
+ // Loading message
88
+ $(this).html('<ul class="jqueryFileTree start"><li class="wait">' + o.loadMessage + '<li></ul>');
89
+ // Get the initial file list
90
+ showTree( $(this), escape(o.root) );
91
+ });
92
+ }
93
+ });
94
+
95
+ })(jQuery);
download-monitor.php ADDED
@@ -0,0 +1,535 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Download Monitor
4
+ Plugin URI: http://mikejolley.com/projects/download-monitor/
5
+ Description: A full solution for managing downloadable files, monitoring downloads and outputting download links and file information on your WordPress powered site.
6
+ Version: 1.0.0
7
+ Author: Mike Jolley
8
+ Author URI: http://mikejolley.com
9
+ Requires at least: 3.5
10
+ Tested up to: 3.5
11
+
12
+ Copyright: � 2013 Mike Jolley.
13
+ License: GNU General Public License v3.0
14
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
15
+
16
+ Filetype icons from the Fugue Icon Pack by Yusuke Kamiyamane.
17
+ */
18
+
19
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
20
+
21
+ /**
22
+ * WP_Download_Monitor class.
23
+ *
24
+ * Main Class which inits the CPT and plugin
25
+ */
26
+ class WP_DLM {
27
+
28
+ private $plugin_url;
29
+ private $plugin_path;
30
+ private $_inline_js;
31
+
32
+ /**
33
+ * __construct function.
34
+ *
35
+ * @access public
36
+ * @return void
37
+ */
38
+ public function __construct() {
39
+ global $wpdb;
40
+
41
+ // Define constants
42
+ define( 'DLM_VERSION', '1.0.0' );
43
+
44
+ // Table for logs
45
+ $wpdb->download_log = $wpdb->prefix . 'download_log';
46
+
47
+ // Include required files
48
+ if ( is_admin() )
49
+ include_once( 'includes/admin/class-dlm-admin.php' );
50
+
51
+ if ( defined('DOING_AJAX') )
52
+ include_once( 'includes/class-dlm-ajax-handler.php' );
53
+
54
+ if ( get_option( 'dlm_enable_logging' ) == 1 )
55
+ include_once( 'includes/class-dlm-logging.php' );
56
+
57
+ include_once( 'includes/class-dlm-download.php' );
58
+ include_once( 'includes/class-dlm-download-version.php' );
59
+ include_once( 'includes/class-dlm-download-handler.php' );
60
+ include_once( 'includes/class-dlm-shortcodes.php' );
61
+
62
+ // Activation
63
+ register_activation_hook( __FILE__, array( $this, 'init_user_roles' ), 10 );
64
+ register_activation_hook( __FILE__, array( $this, 'init_taxonomy' ), 10 );
65
+ register_activation_hook( __FILE__, array( $this, 'install_tables' ), 10 );
66
+ register_activation_hook( __FILE__, array( $this, 'directory_protection' ), 10 );
67
+ register_activation_hook( __FILE__, array( $GLOBALS['DLM_Download_Handler'], 'add_endpoint' ), 10 );
68
+ register_activation_hook( __FILE__, 'flush_rewrite_rules', 11 );
69
+
70
+ // Actions
71
+ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_links' ) );
72
+ add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
73
+ add_action( 'init', array( $this, 'register_globals' ) );
74
+ add_action( 'init', array( $this, 'init_taxonomy' ) );
75
+ add_action( 'after_setup_theme', array( $this, 'compatibility' ) );
76
+ add_action( 'the_post', array( $this, 'setup_download_data' ) );
77
+ add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
78
+ add_action( 'wp_footer', array( $this, 'output_inline_js' ), 25 );
79
+ add_action( 'admin_footer', array( $this, 'output_inline_js' ), 25 );
80
+ add_action( 'widgets_init', array( $this, 'register_widgets' ) );
81
+ }
82
+
83
+ /**
84
+ * Add links to admin plugins page.
85
+ * @param array $links
86
+ * @return array
87
+ */
88
+ public function plugin_links( $links ) {
89
+ $plugin_links = array(
90
+ '<a href="' . admin_url( 'edit.php?post_type=dlm_download&page=download-monitor-settings' ) . '">' . __( 'Settings', 'download_monitor' ) . '</a>',
91
+ '<a href="http://mikejolley.com/projects/download-monitor/add-ons/">' . __( 'Add-ons', 'download_monitor' ) . '</a>',
92
+ '<a href="https://github.com/mikejolley/download-monitor/wiki">' . __( 'Docs', 'download_monitor' ) . '</a>',
93
+ );
94
+ return array_merge( $plugin_links, $links );
95
+ }
96
+
97
+ /**
98
+ * frontend_scripts function.
99
+ *
100
+ * @access public
101
+ * @return void
102
+ */
103
+ public function frontend_scripts() {
104
+ wp_enqueue_style( 'dlm-frontend', $this->plugin_url() . '/assets/css/frontend.css' );
105
+ }
106
+
107
+ /**
108
+ * Localisation
109
+ *
110
+ * @access private
111
+ * @return void
112
+ */
113
+ public function load_plugin_textdomain() {
114
+ load_textdomain( 'download_monitor', WP_LANG_DIR . '/download-monitor/download_monitor-' . get_locale() . '.mo' );
115
+ load_plugin_textdomain( 'download_monitor', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
116
+ }
117
+
118
+ /**
119
+ * Register environment globals
120
+ *
121
+ * @access private
122
+ * @return void
123
+ */
124
+ public function register_globals() {
125
+ $GLOBALS['dlm_download'] = null;
126
+ }
127
+
128
+ /**
129
+ * When the_post is called, get product data too
130
+ *
131
+ * @access public
132
+ * @param mixed $post
133
+ * @return void
134
+ */
135
+ public function setup_download_data( $post ) {
136
+ if ( is_int( $post ) )
137
+ $post = get_post( $post );
138
+
139
+ if ( $post->post_type !== 'dlm_download' )
140
+ return;
141
+
142
+ $GLOBALS['dlm_download'] = new DLM_Download( $post->ID );
143
+ }
144
+
145
+ /**
146
+ * Add Theme Compatibility
147
+ *
148
+ * @access public
149
+ * @return void
150
+ */
151
+ public function compatibility() {
152
+ // Post thumbnail support
153
+ if ( ! current_theme_supports( 'post-thumbnails' ) ) {
154
+ add_theme_support( 'post-thumbnails' );
155
+ remove_post_type_support( 'post', 'thumbnail' );
156
+ remove_post_type_support( 'page', 'thumbnail' );
157
+ } else {
158
+ add_post_type_support( 'download', 'thumbnail' );
159
+ }
160
+ }
161
+
162
+ /**
163
+ * install_tables function.
164
+ *
165
+ * @access public
166
+ * @return void
167
+ */
168
+ public function install_tables() {
169
+ global $wpdb;
170
+
171
+ $wpdb->hide_errors();
172
+
173
+ $collate = '';
174
+
175
+ if ( $wpdb->has_cap( 'collation' ) ) {
176
+ if( ! empty( $wpdb->charset ) )
177
+ $collate .= "DEFAULT CHARACTER SET $wpdb->charset";
178
+ if( ! empty( $wpdb->collate ) )
179
+ $collate .= " COLLATE $wpdb->collate";
180
+ }
181
+
182
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
183
+
184
+ // WooCommerce Tables
185
+ $dlm_tables = "
186
+ CREATE TABLE {$wpdb->download_log} (
187
+ ID bigint(20) NOT NULL auto_increment,
188
+ type varchar(200) NOT NULL default 'download',
189
+ user_id bigint(20) NOT NULL,
190
+ user_ip varchar(200) NOT NULL,
191
+ user_agent varchar(200) NOT NULL,
192
+ download_id bigint(20) NOT NULL,
193
+ version_id bigint(20) NOT NULL,
194
+ version varchar(200) NOT NULL,
195
+ download_date datetime NOT NULL default '0000-00-00 00:00:00',
196
+ download_status varchar(200) NULL,
197
+ download_status_message varchar(200) NULL,
198
+ PRIMARY KEY (ID),
199
+ KEY attribute_name (download_id)
200
+ ) $collate;
201
+ ";
202
+ dbDelta( $dlm_tables );
203
+ }
204
+
205
+ /**
206
+ * Init user roles
207
+ *
208
+ * @access public
209
+ * @return void
210
+ */
211
+ public function init_user_roles() {
212
+ global $wp_roles;
213
+
214
+ if ( class_exists('WP_Roles') && ! isset( $wp_roles ) )
215
+ $wp_roles = new WP_Roles();
216
+
217
+ if ( is_object( $wp_roles ) ) {
218
+ $wp_roles->add_cap( 'administrator', 'manage_downloads' );
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Init taxonomies
224
+ *
225
+ * @access public
226
+ * @return void
227
+ */
228
+ public function init_taxonomy() {
229
+
230
+ if ( post_type_exists( "dlm_download" ) )
231
+ return;
232
+ /**
233
+ * Taxonomies
234
+ */
235
+ register_taxonomy( 'dlm_download_category',
236
+ array( 'dlm_download' ),
237
+ array(
238
+ 'hierarchical' => true,
239
+ 'update_count_callback' => '_update_post_term_count',
240
+ 'label' => __( 'Categories', 'download_monitor'),
241
+ 'labels' => array(
242
+ 'name' => __( 'Categories', 'download_monitor'),
243
+ 'singular_name' => __( 'Download Category', 'download_monitor'),
244
+ 'search_items' => __( 'Search Download Categories', 'download_monitor'),
245
+ 'all_items' => __( 'All Download Categories', 'download_monitor'),
246
+ 'parent_item' => __( 'Parent Download Category', 'download_monitor'),
247
+ 'parent_item_colon' => __( 'Parent Download Category:', 'download_monitor'),
248
+ 'edit_item' => __( 'Edit Download Category', 'download_monitor'),
249
+ 'update_item' => __( 'Update Download Category', 'download_monitor'),
250
+ 'add_new_item' => __( 'Add New Download Category', 'download_monitor'),
251
+ 'new_item_name' => __( 'New Download Category Name', 'download_monitor')
252
+ ),
253
+ 'show_ui' => true,
254
+ 'query_var' => true,
255
+ 'capabilities' => array(
256
+ 'manage_terms' => 'manage_downloads',
257
+ 'edit_terms' => 'manage_downloads',
258
+ 'delete_terms' => 'manage_downloads',
259
+ 'assign_terms' => 'manage_downloads',
260
+ ),
261
+ 'rewrite' => false,
262
+ )
263
+ );
264
+
265
+ register_taxonomy( 'dlm_download_tag',
266
+ array( 'dlm_download' ),
267
+ array(
268
+ 'hierarchical' => false,
269
+ 'label' => __( 'Tags', 'download_monitor'),
270
+ 'labels' => array(
271
+ 'name' => __( 'Tags', 'download_monitor'),
272
+ 'singular_name' => __( 'Download Tag', 'download_monitor'),
273
+ 'search_items' => __( 'Search Download Tags', 'download_monitor'),
274
+ 'all_items' => __( 'All Download Tags', 'download_monitor'),
275
+ 'parent_item' => __( 'Parent Download Tag', 'download_monitor'),
276
+ 'parent_item_colon' => __( 'Parent Download Tag:', 'download_monitor'),
277
+ 'edit_item' => __( 'Edit Download Tag', 'download_monitor'),
278
+ 'update_item' => __( 'Update Download Tag', 'download_monitor'),
279
+ 'add_new_item' => __( 'Add New Download Tag', 'download_monitor'),
280
+ 'new_item_name' => __( 'New Download Tag Name', 'download_monitor')
281
+ ),
282
+ 'show_ui' => true,
283
+ 'query_var' => true,
284
+ 'capabilities' => array(
285
+ 'manage_terms' => 'manage_downloads',
286
+ 'edit_terms' => 'manage_downloads',
287
+ 'delete_terms' => 'manage_downloads',
288
+ 'assign_terms' => 'manage_downloads',
289
+ ),
290
+ 'rewrite' => false,
291
+ )
292
+ );
293
+
294
+ /**
295
+ * Post Types
296
+ */
297
+ register_post_type( "dlm_download",
298
+ apply_filters( 'dlm_cpt_dlm_download_args', array(
299
+ 'labels' => array(
300
+ 'name' => __( 'Downloads', 'download_monitor' ),
301
+ 'singular_name' => __( 'Download', 'download_monitor' ),
302
+ 'add_new' => __( 'Add New', 'download_monitor' ),
303
+ 'add_new_item' => __( 'Add Download', 'download_monitor' ),
304
+ 'edit' => __( 'Edit', 'download_monitor' ),
305
+ 'edit_item' => __( 'Edit Download', 'download_monitor' ),
306
+ 'new_item' => __( 'New Download', 'download_monitor' ),
307
+ 'view' => __( 'View Download', 'download_monitor' ),
308
+ 'view_item' => __( 'View Download', 'download_monitor' ),
309
+ 'search_items' => __( 'Search Downloads', 'download_monitor' ),
310
+ 'not_found' => __( 'No Downloads found', 'download_monitor' ),
311
+ 'not_found_in_trash' => __( 'No Downloads found in trash', 'download_monitor' ),
312
+ 'parent' => __( 'Parent Download', 'download_monitor' )
313
+ ),
314
+ 'description' => __( 'This is where you can create and manage downloads for your site.', 'download_monitor' ),
315
+ 'public' => false,
316
+ 'show_ui' => true,
317
+ 'capability_type' => 'post',
318
+ 'capabilities' => array(
319
+ 'publish_posts' => 'manage_downloads',
320
+ 'edit_posts' => 'manage_downloads',
321
+ 'edit_others_posts' => 'manage_downloads',
322
+ 'delete_posts' => 'manage_downloads',
323
+ 'delete_others_posts' => 'manage_downloads',
324
+ 'read_private_posts' => 'manage_downloads',
325
+ 'edit_post' => 'manage_downloads',
326
+ 'delete_post' => 'manage_downloads',
327
+ 'read_post' => 'manage_downloads'
328
+ ),
329
+ 'publicly_queryable' => false,
330
+ 'exclude_from_search' => false,
331
+ 'hierarchical' => false,
332
+ 'rewrite' => false,
333
+ 'query_var' => true,
334
+ 'supports' => apply_filters( 'dlm_cpt_dlm_download_supports', array( 'title', 'editor', 'excerpt', 'thumbnail', 'custom-fields' ) ),
335
+ 'has_archive' => false,
336
+ 'show_in_nav_menus' => false
337
+ ) )
338
+ );
339
+
340
+ register_post_type( "dlm_download_version",
341
+ apply_filters( 'dlm_cpt_dlm_download_version_args', array(
342
+ 'public' => false,
343
+ 'show_ui' => false,
344
+ 'publicly_queryable' => false,
345
+ 'exclude_from_search' => true,
346
+ 'hierarchical' => false,
347
+ 'rewrite' => false,
348
+ 'query_var' => false,
349
+ 'show_in_nav_menus' => false
350
+ ) )
351
+ );
352
+ }
353
+
354
+ /**
355
+ * register_widgets function.
356
+ *
357
+ * @access public
358
+ * @return void
359
+ */
360
+ function register_widgets() {
361
+ include_once( 'includes/widgets/class-dlm-widget-downloads.php' );
362
+
363
+ register_widget( 'DLM_Widget_Downloads' );
364
+ }
365
+
366
+ /** Helper functions *****************************************************/
367
+
368
+ /**
369
+ * get_template_part function.
370
+ *
371
+ * @access public
372
+ * @param mixed $slug
373
+ * @param string $name (default: '')
374
+ * @return void
375
+ */
376
+ public function get_template_part( $slug, $name = '', $custom_dir = '' ) {
377
+ $template = '';
378
+
379
+ // Look in yourtheme/slug-name.php and yourtheme/download-monitor/slug-name.php
380
+ if ( $name )
381
+ $template = locate_template( array ( "{$slug}-{$name}.php", "download-monitor/{$slug}-{$name}.php" ) );
382
+
383
+ // Get default slug-name.php
384
+ if ( ! $template && $name && file_exists( $this->plugin_path() . "/templates/{$slug}-{$name}.php" ) )
385
+ $template = $this->plugin_path() . "/templates/{$slug}-{$name}.php";
386
+
387
+ // If a custom path was defined, check that next
388
+ if ( ! $template && $custom_dir && file_exists( trailingslashit( $custom_dir ) . "{$slug}-{$name}.php" ) )
389
+ $template = trailingslashit( $custom_dir ) . "{$slug}-{$name}.php";
390
+
391
+ // If template file doesn't exist, look in yourtheme/slug.php and yourtheme/download-monitor/slug.php
392
+ if ( ! $template )
393
+ $template = locate_template( array( "{$slug}.php", "download-monitor/{$slug}.php" ) );
394
+
395
+ // If a custom path was defined, check that next
396
+ if ( ! $template && $custom_dir && file_exists( trailingslashit( $custom_dir ) . "{$slug}-{$name}.php" ) )
397
+ $template = trailingslashit( $custom_dir ) . "{$slug}.php";
398
+
399
+ // Get default slug-name.php
400
+ if ( ! $template && file_exists( $this->plugin_path() . "/templates/{$slug}.php" ) )
401
+ $template = $this->plugin_path() . "/templates/{$slug}.php";
402
+
403
+ if ( $template )
404
+ load_template( $template, false );
405
+ }
406
+
407
+ /**
408
+ * Get the plugin url
409
+ *
410
+ * @access public
411
+ * @return string
412
+ */
413
+ public function plugin_url() {
414
+ if ( $this->plugin_url )
415
+ return $this->plugin_url;
416
+
417
+ return $this->plugin_url = plugins_url( basename( plugin_dir_path(__FILE__) ), basename( __FILE__ ) );
418
+ }
419
+
420
+ /**
421
+ * Get the plugin path
422
+ *
423
+ * @access public
424
+ * @return string
425
+ */
426
+ public function plugin_path() {
427
+ if ( $this->plugin_path )
428
+ return $this->plugin_path;
429
+
430
+ return $this->plugin_path = plugin_dir_path( __FILE__ );
431
+ }
432
+
433
+ /**
434
+ * Enqueue JS to be added to the footer.
435
+ *
436
+ * @access public
437
+ * @param mixed $code
438
+ * @return void
439
+ */
440
+ public function add_inline_js( $code ) {
441
+ $this->_inline_js .= "\n" . $code . "\n";
442
+ }
443
+
444
+ /**
445
+ * Output enqueued JS
446
+ *
447
+ * @access public
448
+ * @return void
449
+ */
450
+ public function output_inline_js() {
451
+ if ( $this->_inline_js ) {
452
+ echo "<!-- Download Monitor JavaScript-->\n<script type=\"text/javascript\">\njQuery(document).ready(function($) {";
453
+ echo $this->_inline_js;
454
+ echo "});\n</script>\n";
455
+ $this->_inline_js = '';
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep.
461
+ * The depth of the recursiveness can be controlled by the $levels param.
462
+ *
463
+ * @access public
464
+ * @param string $folder (default: '')
465
+ * @return void
466
+ */
467
+ function list_files( $folder = '' ) {
468
+ if ( empty($folder) )
469
+ return false;
470
+
471
+ $files = array();
472
+ if ( $dir = @opendir( $folder ) ) {
473
+ while (($file = readdir( $dir ) ) !== false ) {
474
+ if ( in_array($file, array('.', '..') ) )
475
+ continue;
476
+ if ( is_dir( $folder . '/' . $file ) ) {
477
+
478
+ $files[] = array(
479
+ 'type' => 'folder',
480
+ 'path' => $folder . '/' . $file
481
+ );
482
+
483
+ } else {
484
+
485
+ $files[] = array(
486
+ 'type' => 'file',
487
+ 'path' => $folder . '/' . $file
488
+ );
489
+
490
+ }
491
+ }
492
+ }
493
+ @closedir( $dir );
494
+ return $files;
495
+ }
496
+
497
+ /**
498
+ * Protect the upload dir on activation.
499
+ *
500
+ * @access public
501
+ * @return void
502
+ */
503
+ public function directory_protection() {
504
+
505
+ // Install files and folders for uploading files and prevent hotlinking
506
+ $upload_dir = wp_upload_dir();
507
+
508
+ $files = array(
509
+ array(
510
+ 'base' => $upload_dir['basedir'] . '/dlm_uploads',
511
+ 'file' => '.htaccess',
512
+ 'content' => 'deny from all'
513
+ ),
514
+ array(
515
+ 'base' => $upload_dir['basedir'] . '/dlm_uploads',
516
+ 'file' => 'index.html',
517
+ 'content' => ''
518
+ )
519
+ );
520
+
521
+ foreach ( $files as $file ) {
522
+ if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
523
+ if ( $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'w' ) ) {
524
+ fwrite( $file_handle, $file['content'] );
525
+ fclose( $file_handle );
526
+ }
527
+ }
528
+ }
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Init download_monitor class
534
+ */
535
+ $GLOBALS['download_monitor'] = new WP_DLM();
includes/admin/class-dlm-admin-cpt.php ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Admin_CPT class.
7
+ */
8
+ class DLM_Admin_CPT {
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function __construct() {
17
+ add_action( "restrict_manage_posts", array( $this, "downloads_by_category" ) );
18
+ add_action( 'delete_post', array( $this, 'delete_post' ) );
19
+ add_filter( 'enter_title_here', array( $this, 'enter_title_here' ), 1, 2 );
20
+ add_filter( 'manage_edit-dlm_download_columns', array( $this, 'columns' ) );
21
+ add_action( 'manage_dlm_download_posts_custom_column', array( $this, 'custom_columns' ), 2 );
22
+ add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
23
+ add_filter( 'manage_edit-dlm_download_sortable_columns', array( $this, 'sortable_columns' ) );
24
+ add_filter( 'request', array( $this, 'sort_columns' ) );
25
+ }
26
+
27
+ /**
28
+ * downloads_by_category function.
29
+ *
30
+ * @access public
31
+ * @param int $show_counts (default: 1)
32
+ * @param int $hierarchical (default: 1)
33
+ * @param int $show_uncategorized (default: 1)
34
+ * @param string $orderby (default: '')
35
+ * @return void
36
+ */
37
+ public function downloads_by_category( $show_counts = 1, $hierarchical = 1, $show_uncategorized = 1, $orderby = '' ) {
38
+ global $typenow, $wp_query;
39
+
40
+ if ( $typenow != 'dlm_download' )
41
+ return;
42
+
43
+ include_once( 'class-dlm-category-walker.php' );
44
+
45
+ $r = array();
46
+ $r['pad_counts'] = 1;
47
+ $r['hierarchical'] = $hierarchical;
48
+ $r['hide_empty'] = 1;
49
+ $r['show_count'] = $show_counts;
50
+ $r['selected'] = ( isset( $wp_query->query['dlm_download_category'] ) ) ? $wp_query->query['dlm_download_category'] : '';
51
+
52
+ $r['menu_order'] = false;
53
+
54
+ if ( $orderby == 'order' )
55
+ $r['menu_order'] = 'asc';
56
+ elseif ( $orderby )
57
+ $r['orderby'] = $orderby;
58
+
59
+ $terms = get_terms( 'dlm_download_category', $r );
60
+
61
+ if (!$terms) return;
62
+
63
+ $output = "<select name='dlm_download_category' id='dropdown_dlm_download_category'>";
64
+ $output .= '<option value="" ' . selected( isset( $_GET['dlm_download_category'] ) ? $_GET['dlm_download_category'] : '', '', false ) . '>'.__( 'Select a category', 'download_monitor' ).'</option>';
65
+ $output .= $this->walk_category_dropdown_tree( $terms, 0, $r );
66
+ $output .="</select>";
67
+
68
+ echo $output;
69
+ }
70
+
71
+ /**
72
+ * Walk the Product Categories.
73
+ *
74
+ * @access public
75
+ * @return void
76
+ */
77
+ private function walk_category_dropdown_tree() {
78
+ $args = func_get_args();
79
+
80
+ // the user's options are the third parameter
81
+ if ( empty($args[2]['walker']) || !is_a($args[2]['walker'], 'Walker') )
82
+ $walker = new DLM_Category_Walker;
83
+ else
84
+ $walker = $args[2]['walker'];
85
+
86
+ return call_user_func_array( array( $walker, 'walk' ), $args );
87
+ }
88
+ /**
89
+ * delete_post function.
90
+ *
91
+ * @access public
92
+ * @param mixed $id
93
+ * @return void
94
+ */
95
+ public function delete_post( $id ) {
96
+ global $wpdb;
97
+
98
+ if ( ! current_user_can( 'delete_posts' ) )
99
+ return;
100
+
101
+ if ( $id > 0 ) {
102
+
103
+ $post_type = get_post_type( $id );
104
+
105
+ switch( $post_type ) {
106
+ case 'dlm_download' :
107
+ if ( $versions =& get_children( 'post_parent=' . $id . '&post_type=dlm_download_version' ) )
108
+ if ( $versions )
109
+ foreach ( $versions as $child )
110
+ wp_delete_post( $child->ID, true );
111
+ break;
112
+ }
113
+ }
114
+ }
115
+
116
+ /**
117
+ * enter_title_here function.
118
+ *
119
+ * @access public
120
+ * @return void
121
+ */
122
+ public function enter_title_here( $text, $post ) {
123
+ if ( $post->post_type == 'dlm_download' )
124
+ return __( 'Download title', 'download_monitor' );
125
+ return $text;
126
+ }
127
+
128
+ /**
129
+ * post_updated_messages function.
130
+ *
131
+ * @access public
132
+ * @param mixed $messages
133
+ * @return void
134
+ */
135
+ public function post_updated_messages( $messages ) {
136
+ global $post, $post_ID;
137
+
138
+ $messages['dlm_download'] = array(
139
+ 0 => '', // Unused. Messages start at index 1.
140
+ 1 => __('Download updated.', 'download_monitor'),
141
+ 2 => __('Custom field updated.', 'download_monitor'),
142
+ 3 => __('Custom field deleted.', 'download_monitor'),
143
+ 4 => __('Download updated.', 'download_monitor'),
144
+ 5 => isset($_GET['revision']) ? sprintf( __('Download restored to revision from %s', 'download_monitor'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
145
+ 6 => __('Download published.', 'download_monitor'),
146
+ 7 => __('Download saved.', 'download_monitor'),
147
+ 8 => __('Download submitted.', 'download_monitor'),
148
+ 9 => sprintf( __('Download scheduled for: <strong>%1$s</strong>.', 'download_monitor'),
149
+ date_i18n( __( 'M j, Y @ G:i', 'download_monitor' ), strtotime( $post->post_date ) ) ),
150
+ 10 => __('Download draft updated.', 'download_monitor'),
151
+ );
152
+
153
+ return $messages;
154
+ }
155
+
156
+ /**
157
+ * columns function.
158
+ *
159
+ * @access public
160
+ * @param mixed $columns
161
+ * @return void
162
+ */
163
+ public function columns( $columns ) {
164
+ global $download_monitor;
165
+
166
+ $columns = array();
167
+
168
+ $columns["cb"] = "<input type=\"checkbox\" />";
169
+ $columns["thumb"] = __("Image", 'download_monitor');
170
+ $columns["title"] = __("Title", 'download_monitor');
171
+ $columns["id"] = __("ID", 'download_monitor');
172
+ $columns["file"] = __("File", 'download_monitor');
173
+ $columns["version"] = __("Version", 'download_monitor');
174
+ $columns["download_cat"] = __("Categories", 'download_monitor');
175
+ $columns["download_tag"] = __("Tags", 'download_monitor');
176
+
177
+ $columns["download_count"] = '<img src="' . $download_monitor->plugin_url() . '/assets/images/download_count_head.png" alt="' . __("download_count", 'download_monitor') . '" />';
178
+ $columns["featured"] = '<img src="' . $download_monitor->plugin_url() . '/assets/images/featured_head.png" alt="' . __("Featured", 'download_monitor') . '" />';
179
+ $columns["members_only"] = '<img src="' . $download_monitor->plugin_url() . '/assets/images/member_head.png" alt="' . __("Members only", 'download_monitor') . '" />';
180
+
181
+ $columns["date"] = __("Date posted", 'download_monitor');
182
+
183
+ return $columns;
184
+ }
185
+
186
+ /**
187
+ * custom_columns function.
188
+ *
189
+ * @access public
190
+ * @param mixed $column
191
+ * @return void
192
+ */
193
+ public function custom_columns( $column ) {
194
+ global $post, $download_monitor;
195
+
196
+ $download = new DLM_Download( $post->ID );
197
+ $file = $download->get_file_version();
198
+
199
+ switch ($column) {
200
+ case "thumb" :
201
+ echo $download->get_the_image();
202
+ break;
203
+ case "id" :
204
+ echo $post->ID;
205
+ break;
206
+ case "download_cat" :
207
+ if ( ! $terms = get_the_term_list( $post->ID, 'dlm_download_category', '', ', ', '' ) ) echo '<span class="na">&ndash;</span>'; else echo $terms;
208
+ break;
209
+ case "download_tag" :
210
+ if ( ! $terms = get_the_term_list( $post->ID, 'dlm_download_tag', '', ', ', '' ) ) echo '<span class="na">&ndash;</span>'; else echo $terms;
211
+ break;
212
+ case "featured" :
213
+
214
+ if ( $download->is_featured() )
215
+ echo '<img src="' . $download_monitor->plugin_url() . '/assets/images/on.png" alt="yes" />';
216
+ else
217
+ echo '<span class="na">&ndash;</span>';
218
+
219
+ break;
220
+ case "members_only" :
221
+
222
+ if ( $download->is_members_only() )
223
+ echo '<img src="' . $download_monitor->plugin_url() . '/assets/images/on.png" alt="yes" />';
224
+ else
225
+ echo '<span class="na">&ndash;</span>';
226
+
227
+ break;
228
+ case "file" :
229
+ if ( $file )
230
+ echo '<code>' . $file->filename . '</code>';
231
+ else
232
+ echo '<span class="na">&ndash;</span>';
233
+ break;
234
+ case "version" :
235
+ if ( $file )
236
+ echo $file->version;
237
+ else
238
+ echo '<span class="na">&ndash;</span>';
239
+ break;
240
+ case "download_count" :
241
+ echo number_format( $download->get_the_download_count(), 0, '.', ',' );
242
+ break;
243
+ case "featured" :
244
+
245
+ if ( $download->is_featured() )
246
+ echo '<img src="' . $download_monitor->plugin_url() . '/assets/images/on.png" alt="yes" />';
247
+ else
248
+ echo '<span class="na">&ndash;</span>';
249
+
250
+ break;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * sortable_columns function.
256
+ *
257
+ * @access public
258
+ * @param mixed $columns
259
+ * @return void
260
+ */
261
+ public function sortable_columns( $columns ) {
262
+ $custom = array(
263
+ 'id' => 'id',
264
+ 'download_count' => 'download_count',
265
+ 'featured' => 'featured',
266
+ 'members_only' => 'members_only',
267
+ );
268
+ return wp_parse_args( $custom, $columns );
269
+ }
270
+
271
+ /**
272
+ * sort_columns function.
273
+ *
274
+ * @access public
275
+ * @param mixed $vars
276
+ * @return void
277
+ */
278
+ public function sort_columns( $vars ) {
279
+ if ( isset( $vars['orderby'] ) ) {
280
+ if ( 'id' == $vars['orderby'] )
281
+ $vars['orderby'] = 'ID';
282
+
283
+ elseif ( 'download_count' == $vars['orderby'] )
284
+ $vars = array_merge( $vars, array(
285
+ 'meta_key' => '_download_count',
286
+ 'orderby' => 'meta_value_num'
287
+ ) );
288
+
289
+ elseif ( 'featured' == $vars['orderby'] )
290
+ $vars = array_merge( $vars, array(
291
+ 'meta_key' => '_featured',
292
+ 'orderby' => 'meta_value'
293
+ ) );
294
+
295
+ elseif ( 'members_only' == $vars['orderby'] )
296
+ $vars = array_merge( $vars, array(
297
+ 'meta_key' => '_members_only',
298
+ 'orderby' => 'meta_value'
299
+ ) );
300
+ }
301
+ return $vars;
302
+ }
303
+ }
304
+
305
+ new DLM_Admin_CPT();
includes/admin/class-dlm-admin-dashboard.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Admin_Dashboard class.
7
+ */
8
+ class DLM_Admin_Dashboard {
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function __construct() {
17
+
18
+ if ( ! current_user_can( 'manage_options' ) )
19
+ return;
20
+
21
+ wp_add_dashboard_widget( 'dlm_popular_downloads', __( 'Popular Downloads', 'download_monitor' ), array( $this, 'popular_downloads' ) );
22
+ }
23
+
24
+ /**
25
+ * popular_downloads function.
26
+ *
27
+ * @access public
28
+ * @return void
29
+ */
30
+ public function popular_downloads() {
31
+
32
+ $args = array(
33
+ 'post_status' => 'publish',
34
+ 'post_type' => 'dlm_download',
35
+ 'no_found_rows' => 1,
36
+ 'posts_per_page' => 10,
37
+ 'orderby' => 'meta_value',
38
+ 'order' => 'desc',
39
+ 'meta_query' => array(
40
+ array(
41
+ 'key' => '_download_count',
42
+ 'value' => '0',
43
+ 'compare' => '>'
44
+ )
45
+ ),
46
+ 'meta_key' => '_download_count',
47
+ 'fields' => 'ids'
48
+ );
49
+
50
+ $download_ids = get_posts( $args );
51
+
52
+ if ( empty( $download_ids ) )
53
+ echo '<p>' . __( 'There are no stats available yet!', 'download_monitor' ) . '</p>';
54
+
55
+ $downloads = array();
56
+
57
+ foreach ( $download_ids as $download_id ) {
58
+ $downloads[ $download_id ] = get_post_meta( $download_id, '_download_count', true );
59
+ }
60
+
61
+ $max_count = max( $downloads );
62
+
63
+ ?>
64
+ <table class="download_chart" cellpadding="0" cellspacing="0">
65
+ <thead>
66
+ <tr>
67
+ <th scope="col"><?php _e( 'Download', "download_monitor" ); ?></th>
68
+ <th scope="col"><?php _e( 'Download count', "download_monitor" ); ?></th>
69
+ </tr>
70
+ </thead>
71
+ <tbody>
72
+ <?php
73
+ foreach ( $downloads as $download_id => $count ) {
74
+ $download = new DLM_Download( $download_id );
75
+
76
+ $width = $count / ( $max_count ? $max_count : 1 ) * 67;
77
+
78
+ echo '<tr>
79
+ <th scope="row" style="width:25%;"><a href="' . admin_url( 'post.php?post=' . $download_id . '&action=edit' ) . '">' . $download->get_the_title() . '</a></th>
80
+ <td><span class="bar" style="width:' . $width . '%;"></span>' . number_format( $count, 0, '.', ',' ) . '</td>
81
+ </tr>';
82
+ }
83
+ ?>
84
+ </tbody>
85
+ </table>
86
+ <?php
87
+ }
88
+
89
+ }
90
+
91
+ new DLM_Admin_Dashboard();
includes/admin/class-dlm-admin-insert.php ADDED
@@ -0,0 +1,350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ /**
5
+ * DLM_Admin_Insert class.
6
+ */
7
+ class DLM_Admin_Insert {
8
+
9
+ /**
10
+ * __construct function.
11
+ *
12
+ * @access public
13
+ * @return void
14
+ */
15
+ public function __construct() {
16
+ add_action( 'media_buttons', array( $this, 'media_buttons' ), 20 );
17
+ add_action( 'media_upload_add_download', array( $this, 'media_browser' ) );
18
+ }
19
+
20
+ /**
21
+ * media_buttons function.
22
+ *
23
+ * @access public
24
+ * @return void
25
+ */
26
+ public function media_buttons( $editor_id = 'content' ) {
27
+ global $download_monitor, $post;
28
+
29
+ if ( ! isset( $post->post_type ) || ! in_array( $post->post_type, array( 'post', 'page' ) ) )
30
+ return;
31
+
32
+ echo '<a href="#" class="button insert-download add_download" data-editor="' . esc_attr( $editor_id ) . '" title="' . esc_attr__( 'Insert Download', 'download_monitor' ) . '">' . __( 'Insert Download', 'download_monitor' ) . '</a>';
33
+
34
+ ob_start();
35
+ ?>
36
+ jQuery(function(){
37
+ // Browse for file
38
+ jQuery('body').on('click', 'a.add_download', function(e){
39
+
40
+ tb_show('<?php esc_attr_e( 'Insert Download', 'download_monitor' ); ?>', 'media-upload.php?post_id=<?php echo $post->ID; ?>&amp;type=add_download&amp;from=wpdlm01&amp;TB_iframe=true&amp;height=200');
41
+
42
+ return false;
43
+ });
44
+ });
45
+ <?php
46
+
47
+ $js_code = ob_get_clean();
48
+ $download_monitor->add_inline_js( $js_code );
49
+ }
50
+
51
+ /**
52
+ * media_browser function.
53
+ *
54
+ * @access public
55
+ * @return void
56
+ */
57
+ public function media_browser() {
58
+ global $download_monitor;
59
+
60
+ // Enqueue scripts and styles for panel
61
+ wp_enqueue_script( 'chosen', $download_monitor->plugin_url() . '/assets/js/chosen/chosen.jquery.min.js' );
62
+ wp_enqueue_style( 'chosen', $download_monitor->plugin_url() . '/assets/js/chosen/chosen.css' );
63
+ wp_enqueue_style( 'download_monitor_admin_css', $download_monitor->plugin_url() . '/assets/css/admin.css' );
64
+
65
+ wp_enqueue_script( 'common' );
66
+ wp_enqueue_style( 'global' );
67
+ wp_enqueue_style( 'wp-admin' );
68
+ wp_enqueue_style( 'colors' );
69
+ wp_enqueue_script('plupload-all');
70
+
71
+ echo '<!DOCTYPE html><html lang="en"><head><title>' . __( 'Insert Download', 'download_monitor' ) . '</title><meta charset="utf-8" />';
72
+
73
+ do_action( 'admin_print_styles' );
74
+ do_action( 'admin_print_scripts' );
75
+ do_action( 'admin_head' );
76
+
77
+ echo '<body id="insert-download" class="wp-core-ui">';
78
+
79
+ ?>
80
+ <h2 class="nav-tab-wrapper">
81
+ <a href="#insert-shortcode" class="nav-tab nav-tab-active"><?php _e( 'Insert Shortcode', 'download_monitor' ); ?></a><a href="#quick-add" class="nav-tab"><?php _e( 'Quick-add download', 'download_monitor' ); ?></a>
82
+ </h2>
83
+ <?php
84
+
85
+ // Handle quick-add form
86
+ if ( ! empty( $_POST['download_url'] ) && ! empty( $_POST['download_title'] ) && wp_verify_nonce( $_POST['quick-add-nonce'], 'quick-add') ) {
87
+
88
+ $url = stripslashes( $_POST['download_url'] );
89
+ $title = sanitize_text_field( stripslashes( $_POST['download_title'] ) );
90
+ $version = sanitize_text_field( stripslashes( $_POST['download_version'] ) );
91
+
92
+ try {
93
+
94
+ $download = array(
95
+ 'post_title' => $title,
96
+ 'post_content' => '',
97
+ 'post_status' => 'publish',
98
+ 'post_author' => get_current_user_id(),
99
+ 'post_type' => 'dlm_download'
100
+ );
101
+
102
+ $download_id = wp_insert_post( $download );
103
+
104
+ if ( $download_id ) {
105
+
106
+ // Meta
107
+ update_post_meta( $download_id, '_featured', 'no' );
108
+ update_post_meta( $download_id, '_members_only', 'no' );
109
+ update_post_meta( $download_id, '_download_count', 0 );
110
+
111
+ // File
112
+ $file = array(
113
+ 'post_title' => 'Download #' . $download_id . ' File Version',
114
+ 'post_content' => '',
115
+ 'post_status' => 'publish',
116
+ 'post_author' => get_current_user_id(),
117
+ 'post_parent' => $download_id,
118
+ 'post_type' => 'dlm_download_version'
119
+ );
120
+
121
+ $file_id = wp_insert_post( $file );
122
+
123
+ if ( ! $file_id )
124
+ throw new Exception( __( 'Error: File was not created.', 'download_monitor' ) );
125
+
126
+ // Meta
127
+ update_post_meta( $file_id, '_version', $version );
128
+ update_post_meta( $file_id, '_files', array( $url ) );
129
+
130
+ $filesize = null;
131
+
132
+ if ( file_exists( $url ) && ( $filesize = filesize( $url ) ) )
133
+ $filesize = $filesize;
134
+ else {
135
+ $file = wp_remote_head( $url );
136
+
137
+ if ( ! is_wp_error( $file ) && ! empty( $file['headers']['content-length'] ) )
138
+ $filesize = $file['headers']['content-length'];
139
+ }
140
+
141
+ update_post_meta( $file_id, '_filesize', $filesize );
142
+
143
+ echo '<div class="updated"><p>' . __( 'Download successfully created.', 'download_monitor' ) . '</p></div>';
144
+
145
+ } else throw new Exception( __( 'Error: Download was not created.', 'download_monitor' ) );
146
+
147
+ } catch ( Exception $e ) {
148
+ echo '<div class="error"><p>' . $e->getMessage() . "</p></div>";
149
+ }
150
+
151
+ }
152
+
153
+ // Get all downloads
154
+ $downloads = get_posts( array(
155
+ 'post_status' => 'publish',
156
+ 'post_type' => 'dlm_download',
157
+ 'posts_per_page' => -1
158
+ ) );
159
+ ?>
160
+ <form id="insert-shortcode">
161
+
162
+ <p>
163
+ <label for="download_id"><?php _e( 'Choose a download', 'download_monitor' ); ?>:</label>
164
+ <select id="download_id" class="input">
165
+ <?php
166
+ foreach ( $downloads as $download ) {
167
+ $download = new DLM_Download( $download->ID );
168
+
169
+ echo '<option value="' . $download->id . '">#' . $download->id . ' &ndash; ' . $download->get_the_title() . ' &ndash; ' . $download->get_the_filename() .'</option>';
170
+ }
171
+ ?>
172
+ </select>
173
+ </p>
174
+ <p>
175
+ <label for="template_name"><?php _e( 'Template', 'download_monitor' ); ?>:</label>
176
+ <input type="text" id="template_name" value="" class="input" placeholder="<?php _e( 'Template Name', 'download_monitor' ); ?>" />
177
+ <span class="description">
178
+ <?php _e( 'Leaving this blank will use the default <code>content-download.php</code> template file. If you enter, for example, <code>image</code>, the <code>content-download-image.php</code> template will be used instead.', 'download_monitor' ); ?>
179
+ </span>
180
+ </p>
181
+ <p>
182
+ <input type="button" class="button insert_download button-primary button-large" value="<?php _e( 'Insert Shortcode', 'download_monitor' ); ?>" />
183
+ </p>
184
+
185
+ </form>
186
+
187
+ <form id="quick-add" action="" method="post">
188
+
189
+ <!-- Uploader section -->
190
+ <div id="plupload-upload-ui" class="hide-if-no-js">
191
+ <div id="drag-drop-area" style="height:240px">
192
+ <div class="drag-drop-inside">
193
+ <p class="drag-drop-info"><?php _e( 'Drop file here', 'download_monitor' ); ?></p>
194
+ <p><?php _e( 'or', 'download_monitor' ); ?></p>
195
+ <p class="drag-drop-buttons"><input id="plupload-browse-button" type="button" value="<?php esc_attr_e( 'Select File', 'download_monitor' ); ?>" class="button" /></p>
196
+ <p><?php _e( 'or', 'download_monitor' ); ?></p>
197
+ <p><a href="#" class="add_manually"><?php _e( 'Enter URL manually', 'download_monitor' ); ?></a></p>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ <div id="quick-add-details" style="display:none">
202
+ <p>
203
+ <label for="download_url"><?php _e( 'Download URL', 'download_monitor' ); ?>:</label>
204
+ <input type="text" name="download_url" id="download_url" value="" class="download_url input" placeholder="<?php _e( 'Required URL', 'download_monitor' ); ?>" />
205
+ </p>
206
+ <p>
207
+ <label for="download_title"><?php _e( 'Download Title', 'download_monitor' ); ?>:</label>
208
+ <input type="text" name="download_title" id="download_title" value="" class="download_title input" placeholder="<?php _e( 'Required title', 'download_monitor' ); ?>" />
209
+ </p>
210
+ <p>
211
+ <label for="download_version"><?php _e( 'Version', 'download_monitor' ); ?>:</label>
212
+ <input type="text" name="download_version" id="download_version" value="" class="input" placeholder="<?php _e( 'Optional version number', 'download_monitor' ); ?>" />
213
+ </p>
214
+ <p>
215
+ <input type="submit" class="button button-primary button-large" value="<?php _e( 'Save Download', 'download_monitor' ); ?>" />
216
+ <?php wp_nonce_field( 'quick-add', 'quick-add-nonce' ) ?>
217
+ </p>
218
+ </div>
219
+
220
+ </form>
221
+
222
+ <script type="text/javascript">
223
+ jQuery(function() {
224
+
225
+ jQuery('.nav-tab-wrapper a').click(function() {
226
+ jQuery('#insert-shortcode, #quick-add').hide();
227
+ jQuery(jQuery(this).attr('href')).show();
228
+ jQuery('a.nav-tab-active').removeClass('nav-tab-active');
229
+ jQuery(this).addClass('nav-tab-active');
230
+ return false;
231
+ });
232
+
233
+ jQuery('#quick-add').hide();
234
+
235
+ jQuery('#download_id').chosen();
236
+
237
+ jQuery('body').on('click', '.insert_download', function(){
238
+
239
+ var win = window.dialogArguments || opener || parent || top;
240
+
241
+ var download_id = jQuery('#download_id').val();
242
+ var template = jQuery('#template_name').val();
243
+ var shortcode = '[download id="' + jQuery('#download_id').val() + '"';
244
+
245
+ if ( template )
246
+ shortcode = shortcode + ' template="' + template + '"';
247
+
248
+ shortcode = shortcode + ']';
249
+
250
+ win.send_to_editor( shortcode );
251
+
252
+ return false;
253
+ });
254
+
255
+ jQuery('.add_manually').click(function() {
256
+ jQuery('#plupload-upload-ui').slideUp();
257
+ jQuery('#quick-add-details').slideDown();
258
+ return false;
259
+ });
260
+
261
+ <?php
262
+ $plupload_init = array(
263
+ 'runtimes' => 'html5,silverlight,flash,html4',
264
+ 'browse_button' => 'plupload-browse-button',
265
+ 'container' => 'plupload-upload-ui',
266
+ 'drop_element' => 'drag-drop-area',
267
+ 'file_data_name' => 'async-upload',
268
+ 'multiple_queues' => false,
269
+ 'max_file_size' => wp_max_upload_size() . 'b',
270
+ 'url' => admin_url( 'admin-ajax.php' ),
271
+ 'flash_swf_url' => includes_url( 'js/plupload/plupload.flash.swf' ),
272
+ 'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
273
+ 'filters' => array( array( 'title' => __( 'Allowed Files' ), 'extensions' => '*' ) ),
274
+ 'multipart' => true,
275
+ 'urlstream_upload' => true,
276
+
277
+ // additional post data to send to our ajax hook
278
+ 'multipart_params' => array(
279
+ '_ajax_nonce' => wp_create_nonce( 'file-upload' ),
280
+ 'action' => 'download_monitor_insert_panel_upload',
281
+ 'type' => 'dlm_download'
282
+ ),
283
+ );
284
+
285
+ // we should probably not apply this filter, plugins may expect wp's media uploader...
286
+ $plupload_init = apply_filters('plupload_init', $plupload_init);
287
+ ?>
288
+
289
+ // create the uploader and pass the config from above
290
+ var uploader = new plupload.Uploader(<?php echo json_encode( $plupload_init ); ?>);
291
+
292
+ // checks if browser supports drag and drop upload, makes some css adjustments if necessary
293
+ uploader.bind('Init', function(up){
294
+ var uploaddiv = jQuery('#plupload-upload-ui');
295
+
296
+ if ( up.features.dragdrop ) {
297
+ uploaddiv.addClass('drag-drop');
298
+
299
+ jQuery('#drag-drop-area')
300
+ .bind('dragover.wp-uploader', function() {
301
+ uploaddiv.addClass('drag-over');
302
+ })
303
+ .bind('dragleave.wp-uploader, drop.wp-uploader', function() {
304
+ uploaddiv.removeClass('drag-over');
305
+ });
306
+
307
+ } else {
308
+ uploaddiv.removeClass('drag-drop');
309
+ jQuery('#drag-drop-area').unbind('.wp-uploader');
310
+ }
311
+ });
312
+
313
+ uploader.init();
314
+
315
+ // a file was added in the queue
316
+ uploader.bind('FilesAdded', function(up, files) {
317
+ var hundredmb = 100 * 1024 * 1024, max = parseInt(up.settings.max_file_size, 10);
318
+
319
+ plupload.each(files, function(file) {
320
+ if ( max > hundredmb && file.size > hundredmb && up.runtime != 'html5' ) {
321
+ // file size error?
322
+ } else {
323
+ jQuery('.drag-drop-inside').html('<p><?php _e( 'Please wait...', 'download_monitor' ); ?></p>');
324
+ }
325
+ });
326
+
327
+ up.refresh();
328
+ up.start();
329
+ });
330
+
331
+ // a file was uploaded
332
+ uploader.bind('FileUploaded', function( up, file, response ) {
333
+ jQuery('#quick-add-details').find('input.download_url').val( response.response );
334
+ jQuery('#quick-add-details').find('input.download_title').val( basename( response.response ) );
335
+ jQuery('#plupload-upload-ui').slideUp();
336
+ jQuery('#quick-add-details').slideDown();
337
+ });
338
+
339
+ function basename(path) {
340
+ return path.split('/').reverse()[0];
341
+ }
342
+
343
+ });
344
+ </script>
345
+ <?php
346
+ echo '</body></html>';
347
+ }
348
+ }
349
+
350
+ new DLM_Admin_Insert();
includes/admin/class-dlm-admin-media-browser.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Admin_Media_Browser class.
7
+ */
8
+ class DLM_Admin_Media_Browser {
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function __construct() {
17
+ add_action( 'media_upload_downloadable_file_browser', array( $this, 'media_browser' ) );
18
+ }
19
+
20
+ /**
21
+ * media_browser function.
22
+ *
23
+ * @access public
24
+ * @return void
25
+ */
26
+ public function media_browser() {
27
+ global $download_monitor;
28
+
29
+ $files = $download_monitor->list_files( ABSPATH, 1 );
30
+
31
+ echo '<!DOCTYPE html><html lang="en"><head><title>' . __( 'Browse for a file', 'download_monitor' ) . '</title>';
32
+
33
+ do_action( 'admin_print_styles' );
34
+ do_action( 'admin_print_scripts' );
35
+ do_action( 'admin_head' );
36
+
37
+ echo '<meta charset="utf-8" /><link rel="stylesheet" type="text/css" href="' . $download_monitor->plugin_url() . '/assets/css/admin.css" /></head><body>';
38
+
39
+ echo '<ul class="download_monitor_file_browser">';
40
+
41
+ foreach( $files as $found_file ) {
42
+
43
+ $file = pathinfo( $found_file['path'] );
44
+
45
+ if ( $found_file['type'] == 'folder' ) {
46
+
47
+ echo '<li><a href="#" class="folder" data-path="' . trailingslashit( $file['dirname'] ) . $file['basename'] . '">' . $file['basename'] . '</a></li>';
48
+
49
+ } else {
50
+
51
+ $filename = $file['basename'];
52
+ $extension = ( empty( $file['extension'] ) ) ? '' : $file['extension'];
53
+
54
+ if ( substr( $filename, 0, 1 ) == '.' ) continue; // Ignore files starting with . like htaccess
55
+ if ( in_array( $extension, array( '', 'php', 'html', 'htm', 'tmp' ) ) ) continue; // Ignored file types
56
+
57
+ echo '<li><a href="#" class="file filetype-' . sanitize_title( $extension ) . '" data-path="' . trailingslashit( $file['dirname'] ) . $file['basename'] . '">' . $file['basename'] . '</a></li>';
58
+
59
+ }
60
+
61
+ }
62
+
63
+ echo '</ul>';
64
+ ?>
65
+ <script type="text/javascript">
66
+ jQuery(function() {
67
+ jQuery('.download_monitor_file_browser').on('click', 'a', function(){
68
+
69
+ var $link = jQuery(this);
70
+ var $parent = $link.closest('li');
71
+
72
+ if ( $link.is('.file') ) {
73
+
74
+ var win = window.dialogArguments || opener || parent || top;
75
+
76
+ win.send_to_editor( $link.attr('data-path') );
77
+
78
+ } else if ( $link.is('.folder_open') ) {
79
+
80
+ $parent.find('ul').remove();
81
+ $link.removeClass('folder_open');
82
+
83
+ } else {
84
+
85
+ $link.after('<ul class="load_tree loading"></ul>');
86
+
87
+ var data = {
88
+ action: 'download_monitor_list_files',
89
+ path: jQuery(this).attr('data-path'),
90
+ security: '<?php echo wp_create_nonce("list-files"); ?>'
91
+ };
92
+
93
+ jQuery.post('<?php echo admin_url('admin-ajax.php'); ?>', data, function(response) {
94
+
95
+ $link.addClass('folder_open');
96
+
97
+ if ( response ) {
98
+ $parent.find('.load_tree').html( response );
99
+ } else {
100
+ $parent.find('.load_tree').html( '<li class="nofiles"><?php _e('No files found', 'download_monitor'); ?></li>' );
101
+ }
102
+ $parent.find('.load_tree').removeClass('load_tree loading');
103
+
104
+ });
105
+ }
106
+ return false;
107
+ });
108
+ });
109
+ </script>
110
+ <?php
111
+ echo '</body></html>';
112
+ }
113
+
114
+ }
115
+
116
+ new DLM_Admin_Media_Browser();
includes/admin/class-dlm-admin-writepanels.php ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Admin class.
7
+ */
8
+ class DLM_Admin_Writepanels {
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function __construct() {
17
+ add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
18
+ add_action( 'save_post', array( $this, 'save_post' ), 1, 2 );
19
+ add_action( 'dlm_save_meta_boxes', array( $this, 'save_meta_boxes' ), 1, 2 );
20
+ }
21
+
22
+ /**
23
+ * add_meta_boxes function.
24
+ *
25
+ * @access public
26
+ * @return void
27
+ */
28
+ public function add_meta_boxes() {
29
+ add_meta_box( 'download-monitor-options', __( 'Download Options', 'download_monitor' ), array( $this, 'download_options' ), 'dlm_download', 'side', 'high' );
30
+ add_meta_box( 'download-monitor-file', __( 'Downloadable File Versions', 'download_monitor' ), array( $this, 'download_files' ), 'dlm_download', 'normal', 'high' );
31
+
32
+ // Excerpt
33
+ if ( function_exists('wp_editor') ) {
34
+ remove_meta_box( 'postexcerpt', 'dlm_download', 'normal' );
35
+ add_meta_box( 'postexcerpt', __('Short Description', 'download_monitor'), array( $this, 'short_description' ), 'dlm_download', 'normal', 'high' );
36
+ }
37
+ }
38
+
39
+ /**
40
+ * download_options function.
41
+ *
42
+ * @access public
43
+ * @param mixed $post
44
+ * @return void
45
+ */
46
+ public function download_options( $post ) {
47
+ global $post, $thepostid;
48
+
49
+ $thepostid = $post->ID;
50
+
51
+ echo '<div class="dlm_options_panel">';
52
+
53
+ do_action( 'dlm_options_start', $thepostid );
54
+
55
+ echo '<p class="form-field form-field-checkbox">
56
+ <input type="checkbox" name="_featured" id="_featured" ' . checked( get_post_meta( $thepostid, '_featured', true ), 'yes', false ) . ' />
57
+ <label for="_featured">' . __( 'Featured download', 'download_monitor' ) . '</label>
58
+ <span class="description">' . __( 'Mark this download as featured. Used by shortcodes and widgets.', 'download_monitor' ) . '</span>
59
+ </p>';
60
+
61
+ echo '<p class="form-field form-field-checkbox">
62
+ <input type="checkbox" name="_members_only" id="_members_only" ' . checked( get_post_meta( $thepostid, '_members_only', true ), 'yes', false ) . ' />
63
+ <label for="_members_only">' . __( 'Members only', 'download_monitor' ) . '</label>
64
+ <span class="description">' . __( 'Only logged in users will be able to access the file via a download link if this is enabled.', 'download_monitor' ) . '</span>
65
+ </p>';
66
+
67
+ do_action( 'dlm_options_end', $thepostid );
68
+
69
+ echo '</div>';
70
+ }
71
+
72
+ /**
73
+ * download_files function.
74
+ *
75
+ * @access public
76
+ * @return void
77
+ */
78
+ public function download_files() {
79
+ global $post, $download_monitor;
80
+
81
+ wp_nonce_field( 'save_meta_data', 'dlm_nonce' );
82
+ ?>
83
+ <div class="download_monitor_files dlm-metaboxes-wrapper">
84
+
85
+ <p class="toolbar">
86
+ <a href="#" class="button plus add_file"><?php _e('Add version', 'download_monitor'); ?></a>
87
+ <a href="#" class="close_all"><?php _e('Close all', 'download_monitor'); ?></a><a href="#" class="expand_all"><?php _e('Expand all', 'download_monitor'); ?></a>
88
+ </p>
89
+
90
+ <div class="dlm-metaboxes downloadable_files">
91
+ <?php
92
+ $i = -1;
93
+ $files = get_posts( 'post_parent=' . $post->ID . '&post_type=dlm_download_version&orderby=menu_order&order=ASC&post_status=any&numberposts=-1' );
94
+
95
+ if ( $files ) foreach ( $files as $file ) {
96
+
97
+ $i++;
98
+ $file_id = $file->ID;
99
+ $file_version = ( $file_version = get_post_meta( $file->ID, '_version', true ) ) ? $file_version : '';
100
+ $file_post_date = $file->post_date;
101
+ $file_download_count = absint( get_post_meta( $file->ID, '_download_count', true ) );
102
+ $file_urls = array_filter( (array) get_post_meta( $file->ID, '_files', true ) );
103
+
104
+ include( 'html-downloadable-file-version.php' );
105
+ }
106
+ ?>
107
+ </div>
108
+
109
+ </div>
110
+ <?php
111
+ ob_start();
112
+ ?>
113
+ jQuery(function(){
114
+
115
+ // Expand all files
116
+ jQuery('.expand_all').click(function(){
117
+ jQuery(this).closest('.dlm-metaboxes-wrapper').find('.dlm-metabox table').show();
118
+ return false;
119
+ });
120
+
121
+ // Close all files
122
+ jQuery('.close_all').click(function(){
123
+ jQuery(this).closest('.dlm-metaboxes-wrapper').find('.dlm-metabox table').hide();
124
+ return false;
125
+ });
126
+
127
+ // Open/close
128
+ jQuery('.dlm-metaboxes-wrapper').on('click', '.dlm-metabox h3', function(event){
129
+ // If the user clicks on some form input inside the h3, like a select list (for variations), the box should not be toggled
130
+ if (jQuery(event.target).filter(':input, option').length) return;
131
+
132
+ jQuery(this).next('.dlm-metabox-content').toggle();
133
+ });
134
+
135
+ // Closes all to begin
136
+ jQuery('.dlm-metabox.closed').each(function(){
137
+ jQuery(this).find('.dlm-metabox-content').hide();
138
+ });
139
+
140
+ // Date picker
141
+ jQuery( ".date-picker-field" ).datepicker({
142
+ dateFormat: "yy-mm-dd",
143
+ numberOfMonths: 1,
144
+ showButtonPanel: true,
145
+ });
146
+
147
+ // Ordering
148
+ jQuery('.downloadable_files').sortable({
149
+ items:'.downloadable_file',
150
+ cursor:'move',
151
+ axis:'y',
152
+ handle: 'h3',
153
+ scrollSensitivity:40,
154
+ forcePlaceholderSize: true,
155
+ helper: 'clone',
156
+ opacity: 0.65,
157
+ placeholder: 'dlm-metabox-sortable-placeholder',
158
+ start:function(event,ui){
159
+ ui.item.css('background-color','#f6f6f6');
160
+ },
161
+ stop:function(event,ui){
162
+ ui.item.removeAttr('style');
163
+ downloadable_file_row_indexes();
164
+ }
165
+ });
166
+
167
+ function downloadable_file_row_indexes() {
168
+ jQuery('.downloadable_files .downloadable_file').each(function(index, el){
169
+ jQuery('.file_menu_order', el).val( parseInt( jQuery(el).index('.downloadable_files .downloadable_file') ) );
170
+ });
171
+ };
172
+
173
+ // Add a file
174
+ jQuery('.download_monitor_files').on('click', 'a.add_file', function(){
175
+
176
+ jQuery('.download_monitor_files').block({ message: null, overlayCSS: { background: '#fff url(<?php echo $download_monitor->plugin_url(); ?>/assets/images/ajax-loader.gif) no-repeat center', opacity: 0.6 } });
177
+
178
+ var size = jQuery('.downloadable_files .downloadable_file').size();
179
+
180
+ var data = {
181
+ action: 'download_monitor_add_file',
182
+ post_id: <?php echo $post->ID; ?>,
183
+ size: size,
184
+ security: '<?php echo wp_create_nonce("add-file"); ?>'
185
+ };
186
+
187
+ jQuery.post('<?php echo admin_url('admin-ajax.php'); ?>', data, function(response) {
188
+
189
+ jQuery('.downloadable_files').prepend( response );
190
+
191
+ downloadable_file_row_indexes();
192
+
193
+ jQuery('.download_monitor_files').unblock();
194
+
195
+ // Date picker
196
+ jQuery( ".date-picker-field" ).datepicker({
197
+ dateFormat: "yy-mm-dd",
198
+ numberOfMonths: 1,
199
+ showButtonPanel: true,
200
+ });
201
+ });
202
+
203
+ return false;
204
+
205
+ });
206
+
207
+ // Remove a file
208
+ jQuery('.download_monitor_files').on('click', 'button.remove_file', function(e){
209
+ e.preventDefault();
210
+ var answer = confirm('<?php _e( 'Are you sure you want to delete this file?', 'download_monitor' ); ?>');
211
+ if ( answer ) {
212
+
213
+ var el = jQuery(this).closest('.downloadable_file');
214
+ var file_id = el.attr('data-file');
215
+
216
+ if ( file_id > 0 ) {
217
+
218
+ jQuery(el).block({ message: null, overlayCSS: { background: '#fff url(<?php echo $download_monitor->plugin_url(); ?>/assets/images/ajax-loader.gif) no-repeat center', opacity: 0.6 } });
219
+
220
+ var data = {
221
+ action: 'download_monitor_remove_file',
222
+ file_id: file_id,
223
+ download_id: '<?php echo $post->ID; ?>',
224
+ security: '<?php echo wp_create_nonce( "remove-file" ); ?>'
225
+ };
226
+
227
+ jQuery.post('<?php echo admin_url('admin-ajax.php'); ?>', data, function(response) {
228
+ jQuery(el).fadeOut('300').remove();
229
+ });
230
+
231
+ } else {
232
+ jQuery(el).fadeOut('300').remove();
233
+ }
234
+ }
235
+ return false;
236
+ });
237
+
238
+ // Browse for file
239
+ jQuery('.download_monitor_files').on('click', 'a.browse_for_file', function(e){
240
+
241
+ downloadable_files_field = jQuery(this).closest('.downloadable_file').find('textarea[name^="downloadable_file_urls"]');
242
+
243
+ window.send_to_editor = window.send_to_browse_file_url;
244
+
245
+ tb_show('<?php esc_attr_e( 'Browse for a file', 'download_monitor' ); ?>', 'media-upload.php?post_id=<?php echo $post->ID; ?>&amp;type=downloadable_file_browser&amp;from=wpdlm01&amp;TB_iframe=true');
246
+
247
+ return false;
248
+ });
249
+
250
+ window.send_to_browse_file_url = function(html) {
251
+
252
+ if ( html ) {
253
+ old = jQuery.trim( jQuery(downloadable_files_field).val() );
254
+ if ( old ) old = old + "\n";
255
+ jQuery(downloadable_files_field).val( old + html );
256
+ }
257
+
258
+ tb_remove();
259
+
260
+ window.send_to_editor = window.send_to_editor_default;
261
+ }
262
+
263
+ // Uploading files
264
+ var dlm_upload_file_frame;
265
+
266
+ jQuery(document).on( 'click', '.dlm_upload_file', function( event ){
267
+
268
+ var $el = $(this);
269
+ var $file_path_field = $el.parent().parent().find('.downloadable_file_urls');
270
+ var file_paths = $file_path_field.val();
271
+
272
+ event.preventDefault();
273
+
274
+ // If the media frame already exists, reopen it.
275
+ if ( dlm_upload_file_frame ) {
276
+ dlm_upload_file_frame.open();
277
+ return;
278
+ }
279
+
280
+ var downloadable_file_states = [
281
+ // Main states.
282
+ new wp.media.controller.Library({
283
+ library: wp.media.query(),
284
+ multiple: true,
285
+ title: $el.data('choose'),
286
+ priority: 20,
287
+ filterable: 'uploaded',
288
+ })
289
+ ];
290
+
291
+ // Create the media frame.
292
+ dlm_upload_file_frame = wp.media.frames.downloadable_file = wp.media({
293
+ // Set the title of the modal.
294
+ title: $el.data('choose'),
295
+ library: {
296
+ type: ''
297
+ },
298
+ button: {
299
+ text: $el.data('update'),
300
+ },
301
+ multiple: true,
302
+ states: downloadable_file_states,
303
+ });
304
+
305
+ // When an image is selected, run a callback.
306
+ dlm_upload_file_frame.on( 'select', function() {
307
+
308
+ var selection = dlm_upload_file_frame.state().get('selection');
309
+
310
+ selection.map( function( attachment ) {
311
+
312
+ attachment = attachment.toJSON();
313
+
314
+ if ( attachment.url )
315
+ file_paths = file_paths ? file_paths + "\n" + attachment.url : attachment.url
316
+
317
+ } );
318
+
319
+ $file_path_field.val( file_paths );
320
+ });
321
+
322
+ // Set post to 0 and set our custom type
323
+ dlm_upload_file_frame.on( 'ready', function() {
324
+ dlm_upload_file_frame.uploader.options.uploader.params = {
325
+ type: 'dlm_download'
326
+ };
327
+ });
328
+
329
+ // Finally, open the modal.
330
+ dlm_upload_file_frame.open();
331
+ });
332
+
333
+ });
334
+ <?php
335
+ $js_code = ob_get_clean();
336
+ $download_monitor->add_inline_js( $js_code );
337
+ }
338
+
339
+ /**
340
+ * short_description function.
341
+ *
342
+ * @access public
343
+ * @param mixed $post
344
+ * @return void
345
+ */
346
+ public function short_description( $post ) {
347
+ $settings = array(
348
+ 'quicktags' => array( 'buttons' => 'em,strong,link' ),
349
+ 'textarea_name' => 'excerpt',
350
+ 'quicktags' => true,
351
+ 'tinymce' => true,
352
+ 'editor_css' => '<style>#wp-excerpt-editor-container .wp-editor-area{height:200px; width:100%;}</style>'
353
+ );
354
+
355
+ wp_editor( htmlspecialchars_decode( $post->post_excerpt ), 'excerpt', $settings );
356
+ }
357
+
358
+ /**
359
+ * save_post function.
360
+ *
361
+ * @access public
362
+ * @param mixed $post_id
363
+ * @param mixed $post
364
+ * @return void
365
+ */
366
+ public function save_post( $post_id, $post ) {
367
+ if ( empty( $post_id ) || empty( $post ) || empty( $_POST ) ) return;
368
+ if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return;
369
+ if ( is_int( wp_is_post_revision( $post ) ) ) return;
370
+ if ( is_int( wp_is_post_autosave( $post ) ) ) return;
371
+ if ( empty($_POST['dlm_nonce']) || ! wp_verify_nonce( $_POST['dlm_nonce'], 'save_meta_data' ) ) return;
372
+ if ( ! current_user_can( 'edit_post', $post_id ) ) return;
373
+ if ( $post->post_type != 'dlm_download' ) return;
374
+
375
+ do_action( 'dlm_save_meta_boxes', $post_id, $post );
376
+ }
377
+
378
+ /**
379
+ * save function.
380
+ *
381
+ * @access public
382
+ * @param mixed $post_id
383
+ * @param mixed $post
384
+ * @return void
385
+ */
386
+ public function save_meta_boxes( $post_id, $post ) {
387
+ global $wpdb, $download_monitor;
388
+
389
+ // Update options
390
+ $_featured = ( isset( $_POST['_featured'] ) ) ? 'yes' : 'no';
391
+ $_members_only = ( isset( $_POST['_members_only'] ) ) ? 'yes' : 'no';
392
+
393
+ update_post_meta( $post_id, '_featured', $_featured );
394
+ update_post_meta( $post_id, '_members_only', $_members_only );
395
+
396
+ $total_download_count = 0;
397
+
398
+ // Process files
399
+ if ( isset( $_POST['downloadable_file_id'] ) ) {
400
+
401
+ $downloadable_file_id = $_POST['downloadable_file_id'];
402
+ $downloadable_file_menu_order = $_POST['downloadable_file_menu_order'];
403
+ $downloadable_file_version = $_POST['downloadable_file_version'];
404
+ $downloadable_file_urls = $_POST['downloadable_file_urls'];
405
+ $downloadable_file_date = $_POST['downloadable_file_date'];
406
+ $downloadable_file_date_hour = $_POST['downloadable_file_date_hour'];
407
+ $downloadable_file_date_minute = $_POST['downloadable_file_date_minute'];
408
+ $downloadable_file_download_count = $_POST['downloadable_file_download_count'];
409
+
410
+ for ( $i = 0; $i <= max( array_keys( $downloadable_file_id ) ); $i ++ ) {
411
+
412
+ if ( ! isset( $downloadable_file_id[ $i ] ) )
413
+ continue;
414
+
415
+ $file_id = absint( $downloadable_file_id[ $i ] );
416
+ $file_menu_order = absint( $downloadable_file_menu_order[ $i ] );
417
+ $file_version = sanitize_text_field( $downloadable_file_version[ $i ] );
418
+ $file_date_hour = absint( $downloadable_file_date_hour[ $i ] );
419
+ $file_date_minute = absint( $downloadable_file_date_minute[ $i ] );
420
+ $file_date = sanitize_text_field( $downloadable_file_date[ $i ] );
421
+ $file_download_count = sanitize_text_field( $downloadable_file_download_count[ $i ] );
422
+ $files = array_filter( array_map( 'trim', explode( "\n", $downloadable_file_urls[ $i ] ) ) );
423
+
424
+ if ( ! $file_id )
425
+ continue;
426
+
427
+ // Generate a useful post title
428
+ $file_post_title = 'Download #' . $post_id . ' File Version';
429
+
430
+ // Generate date
431
+ if ( empty( $file_date ) ) {
432
+ $date = current_time('timestamp');
433
+ } else {
434
+ $date = strtotime( $file_date . ' ' . $file_date_hour . ':' . $file_date_minute . ':00' );
435
+ }
436
+
437
+ // Update
438
+ $wpdb->update( $wpdb->posts, array(
439
+ 'post_status' => 'publish',
440
+ 'post_title' => $file_post_title,
441
+ 'menu_order' => $file_menu_order,
442
+ 'post_date' => date( 'Y-m-d H:i:s', $date )
443
+ ), array( 'ID' => $file_id ) );
444
+
445
+ // Update post meta
446
+ update_post_meta( $file_id, '_version', $file_version );
447
+ update_post_meta( $file_id, '_files', $files );
448
+
449
+ $filesize = null;
450
+ $url = current( $files );
451
+
452
+ if ( file_exists( $url ) && ( $filesize = filesize( $url ) ) )
453
+ $filesize = $filesize;
454
+ else {
455
+ $file = wp_remote_head( $url );
456
+
457
+ if ( ! is_wp_error( $file ) && ! empty( $file['headers']['content-length'] ) )
458
+ $filesize = $file['headers']['content-length'];
459
+ }
460
+
461
+ update_post_meta( $file_id, '_filesize', $filesize );
462
+
463
+ if ( $file_download_count !== '' ) {
464
+ update_post_meta( $file_id, '_download_count', absint( $file_download_count ) );
465
+ $total_download_count += absint( $file_download_count );
466
+ } else {
467
+ $total_download_count += absint( get_post_meta( $file_id, '_download_count', true ) );
468
+ }
469
+ }
470
+ }
471
+
472
+ // Sync download_count
473
+ update_post_meta( $post_id, '_download_count', $total_download_count );
474
+
475
+ // Clear transients
476
+ delete_transient( 'dlm_children_ids_' . $post_id );
477
+ }
478
+ }
479
+
480
+ new DLM_Admin_Writepanels();
includes/admin/class-dlm-admin.php ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Admin class.
7
+ */
8
+ class DLM_Admin {
9
+
10
+ private $settings;
11
+
12
+ /**
13
+ * __construct function.
14
+ *
15
+ * @access public
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ include_once( 'class-dlm-admin-writepanels.php' );
20
+ include_once( 'class-dlm-admin-media-browser.php' );
21
+ include_once( 'class-dlm-admin-cpt.php' );
22
+ include_once( 'class-dlm-admin-insert.php' );
23
+
24
+ // Directory protection
25
+ add_filter( 'mod_rewrite_rules', array( $this, 'ms_files_protection' ) );
26
+ add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
27
+
28
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
29
+ add_action( 'admin_menu', array( $this, 'admin_menu' ), 12 );
30
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
31
+ add_action( 'admin_init', array( $this, 'export_logs' ) );
32
+ add_action( 'wp_dashboard_setup', array( $this, 'admin_dashboard' ) );
33
+ }
34
+
35
+ /**
36
+ * ms_files_protection function.
37
+ *
38
+ * @access public
39
+ * @param mixed $rewrite
40
+ * @return void
41
+ */
42
+ public function ms_files_protection( $rewrite ) {
43
+ global $wp_rewrite;
44
+
45
+ if ( ! is_multisite() )
46
+ return $rewrite;
47
+
48
+ $rule = "\n# DLM Rules - Protect Files from ms-files.php\n\n";
49
+ $rule .= "<IfModule mod_rewrite.c>\n";
50
+ $rule .= "RewriteEngine On\n";
51
+ $rule .= "RewriteCond %{QUERY_STRING} file=dlm_uploads/ [NC]\n";
52
+ $rule .= "RewriteRule /ms-files.php$ - [F]\n";
53
+ $rule .= "</IfModule>\n\n";
54
+
55
+ return $rule . $rewrite;
56
+ }
57
+
58
+ /**
59
+ * upload_dir function.
60
+ *
61
+ * @access public
62
+ * @param mixed $pathdata
63
+ * @return void
64
+ */
65
+ public function upload_dir( $pathdata ) {
66
+
67
+ if ( isset( $_POST['type'] ) && $_POST['type'] == 'dlm_download' ) {
68
+ $subdir = '/dlm_uploads' . $pathdata['subdir'];
69
+ $pathdata['path'] = str_replace( $pathdata['subdir'], $subdir, $pathdata['path'] );
70
+ $pathdata['url'] = str_replace( $pathdata['subdir'], $subdir, $pathdata['url'] );
71
+ $pathdata['subdir'] = str_replace( $pathdata['subdir'], $subdir, $pathdata['subdir'] );
72
+ }
73
+
74
+ return $pathdata;
75
+ }
76
+
77
+ /**
78
+ * init_settings function.
79
+ *
80
+ * @access private
81
+ * @return void
82
+ */
83
+ private function init_settings() {
84
+ $this->settings = apply_filters( 'download_monitor_settings',
85
+ array(
86
+ 'general' => array(
87
+ __( 'General', 'download_monitor' ),
88
+ array(
89
+ array(
90
+ 'name' => 'dlm_default_template',
91
+ 'std' => '',
92
+ 'placeholder' => __( '', 'download_monitor' ),
93
+ 'label' => __( 'Default Template', 'download_monitor' ),
94
+ 'desc' => __( 'Choose which template is used for <code>[download]</code> shortcodes by default (this can be overridden by the <code>format</code> argument).', 'download_monitor' ) . ' ' . __( 'Leaving this blank will use the default <code>content-download.php</code> template file. If you enter, for example, <code>image</code>, the <code>content-download-image.php</code> template will be used instead.', 'download_monitor' )
95
+ ),
96
+ ),
97
+ ),
98
+ 'endpoints' => array(
99
+ __( 'Endpoint', 'download_monitor' ),
100
+ array(
101
+ array(
102
+ 'name' => 'dlm_download_endpoint',
103
+ 'std' => 'download',
104
+ 'placeholder' => __( 'download', 'download_monitor' ),
105
+ 'label' => __( 'Download Endpoint', 'download_monitor' ),
106
+ 'desc' => sprintf( __( 'Define what endpoint should be used for download links. By default this will be <code>%s</code>.', 'download_monitor' ), home_url( '/download/' ) )
107
+ ),
108
+ array(
109
+ 'name' => 'dlm_download_endpoint_value',
110
+ 'std' => 'ID',
111
+ 'label' => __( 'Endpoint Value', 'download_monitor' ),
112
+ 'desc' => sprintf( __( 'Define what unique value should be used on the end of your endpoint to identify the downloadable file. e.g. ID would give a link like <code>%s</code>', 'download_monitor' ), home_url( '/download/10/' ) ),
113
+ 'type' => 'select',
114
+ 'options' => array(
115
+ 'ID' => __( 'Download ID', 'download_monitor' ),
116
+ 'slug' => __( 'Download slug', 'download_monitor' )
117
+ )
118
+ ),
119
+ array(
120
+ 'name' => 'dlm_xsendfile_enabled',
121
+ 'std' => '',
122
+ 'label' => __( 'X-Accel-Redirect / X-Sendfile', 'download_monitor' ),
123
+ 'cb_label' => __( 'Enable', 'download_monitor' ),
124
+ 'desc' => __( 'If supported, <code>X-Accel-Redirect</code> / <code>X-Sendfile</code> can be used to serve downloads instead of PHP (server requires <code>mod_xsendfile</code>).', 'download_monitor' ),
125
+ 'type' => 'checkbox'
126
+ )
127
+ )
128
+ ),
129
+ 'logging' => array(
130
+ __( 'Logging', 'download_monitor' ),
131
+ array(
132
+ array(
133
+ 'name' => 'dlm_enable_logging',
134
+ 'cb_label' => __( 'Enable', 'download_monitor' ),
135
+ 'std' => '1',
136
+ 'label' => __( 'Download Log', 'download_monitor' ),
137
+ 'desc' => __( 'Log download attempts, IP addresses and more.', 'download_monitor' ),
138
+ 'type' => 'checkbox'
139
+ ),
140
+ array(
141
+ 'name' => 'dlm_ip_blacklist',
142
+ 'std' => '192.168.0.*',
143
+ 'label' => __( 'Blacklist IPs', 'download_monitor' ),
144
+ 'desc' => __( 'List IP Addresses to blacklist, 1 per line. Use <code>*</code> for a wildcard.', 'download_monitor' ),
145
+ 'placeholder' => '',
146
+ 'type' => 'textarea'
147
+ ),
148
+ array(
149
+ 'name' => 'dlm_user_agent_blacklist',
150
+ 'std' => 'Googlebot',
151
+ 'label' => __( 'Blacklist user agents', 'download_monitor' ),
152
+ 'desc' => __( 'List browser user agents to blacklist, 1 per line.', 'download_monitor' ),
153
+ 'placeholder' => '',
154
+ 'type' => 'textarea'
155
+ ),
156
+ )
157
+ )
158
+ )
159
+ );
160
+ }
161
+
162
+ /**
163
+ * register_settings function.
164
+ *
165
+ * @access public
166
+ * @return void
167
+ */
168
+ public function register_settings() {
169
+ $this->init_settings();
170
+
171
+ foreach ( $this->settings as $section ) {
172
+ foreach ( $section[1] as $option ) {
173
+ if ( isset( $option['std'] ) )
174
+ add_option( $option['name'], $option['std'] );
175
+ register_setting( 'download_monitor', $option['name'] );
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * admin_enqueue_scripts function.
182
+ *
183
+ * @access public
184
+ * @return void
185
+ */
186
+ public function admin_enqueue_scripts() {
187
+ global $download_monitor;
188
+
189
+ wp_enqueue_script( 'jquery-blockui', $download_monitor->plugin_url() . '/assets/js/blockui.min.js', '2.39', array( 'jquery' ) );
190
+ wp_enqueue_script( 'jquery-ui-sortable' );
191
+ wp_enqueue_script( 'jquery-ui-datepicker' );
192
+ wp_enqueue_style( 'jquery-ui-style', (is_ssl()) ? 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css' : 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css' );
193
+ wp_enqueue_style( 'download_monitor_admin_css', $download_monitor->plugin_url() . '/assets/css/admin.css' );
194
+ }
195
+
196
+ /**
197
+ * admin_menu function.
198
+ *
199
+ * @access public
200
+ * @return void
201
+ */
202
+ public function admin_menu() {
203
+ if ( get_option( 'dlm_enable_logging' ) == 1 )
204
+ add_submenu_page( 'edit.php?post_type=dlm_download', __( 'Logs', 'download_monitor' ), __( 'Logs', 'download_monitor' ), 'manage_options', 'download-monitor-logs', array( $this, 'log_viewer' ) );
205
+
206
+ add_submenu_page( 'edit.php?post_type=dlm_download', __( 'Settings', 'download_monitor' ), __( 'Settings', 'download_monitor' ), 'manage_options', 'download-monitor-settings', array( $this, 'settings_page' ) );
207
+ }
208
+
209
+ /**
210
+ * settings_page function.
211
+ *
212
+ * @access public
213
+ * @return void
214
+ */
215
+ public function settings_page() {
216
+ global $download_monitor;
217
+
218
+ $this->init_settings();
219
+ ?>
220
+ <div class="wrap">
221
+ <form method="post" action="options.php">
222
+
223
+ <?php settings_fields( 'download_monitor' ); ?>
224
+ <?php screen_icon(); ?>
225
+
226
+ <h2 class="nav-tab-wrapper">
227
+ <?php
228
+ foreach ( $this->settings as $section ) {
229
+ echo '<a href="#settings-' . sanitize_title( $section[0] ) . '" class="nav-tab">' . esc_html( $section[0] ) . '</a>';
230
+ }
231
+ ?>
232
+ </h2><br/>
233
+
234
+ <?php
235
+ if ( ! empty( $_GET['settings-updated'] ) ) {
236
+ flush_rewrite_rules();
237
+ echo '<div class="updated fade"><p>' . __( 'Settings successfully saved', 'download_monitor' ) . '</p></div>';
238
+ }
239
+
240
+ foreach ( $this->settings as $section ) {
241
+
242
+ echo '<div id="settings-' . sanitize_title( $section[0] ) . '" class="settings_panel">';
243
+
244
+ echo '<table class="form-table">';
245
+
246
+ foreach ( $section[1] as $option ) {
247
+
248
+ $placeholder = ( ! empty( $option['placeholder'] ) ) ? 'placeholder="' . $option['placeholder'] . '"' : '';
249
+
250
+ echo '<tr valign="top"><th scope="row"><label for="setting-' . $option['name'] . '">' . $option['label'] . '</a></th><td>';
251
+
252
+ if ( ! isset( $option['type'] ) ) $option['type'] = '';
253
+
254
+ $value = get_option( $option['name'] );
255
+
256
+ switch ( $option['type'] ) {
257
+
258
+ case "checkbox" :
259
+
260
+ ?><label><input id="setting-<?php echo $option['name']; ?>" name="<?php echo $option['name']; ?>" type="checkbox" value="1" <?php checked( '1', $value ); ?> /> <?php echo $option['cb_label']; ?></label><?php
261
+
262
+ if ( $option['desc'] )
263
+ echo ' <p class="description">' . $option['desc'] . '</p>';
264
+
265
+ break;
266
+ case "textarea" :
267
+
268
+ ?><textarea id="setting-<?php echo $option['name']; ?>" class="large-text" cols="50" rows="3" name="<?php echo $option['name']; ?>" <?php echo $placeholder; ?>><?php echo esc_textarea( $value ); ?></textarea><?php
269
+
270
+ if ( $option['desc'] )
271
+ echo ' <p class="description">' . $option['desc'] . '</p>';
272
+
273
+ break;
274
+ case "select" :
275
+
276
+ ?><select id="setting-<?php echo $option['name']; ?>" class="regular-text" name="<?php echo $option['name']; ?>"><?php
277
+ foreach( $option['options'] as $key => $name )
278
+ echo '<option value="' . esc_attr( $key ) . '" ' . selected( $value, $key, false ) . '>' . esc_html( $name ) . '</option>';
279
+ ?></select><?php
280
+
281
+ if ( $option['desc'] )
282
+ echo ' <p class="description">' . $option['desc'] . '</p>';
283
+
284
+ break;
285
+ default :
286
+
287
+ ?><input id="setting-<?php echo $option['name']; ?>" class="regular-text" type="text" name="<?php echo $option['name']; ?>" value="<?php esc_attr_e( $value ); ?>" <?php echo $placeholder; ?> /><?php
288
+
289
+ if ( $option['desc'] )
290
+ echo ' <p class="description">' . $option['desc'] . '</p>';
291
+
292
+ break;
293
+
294
+ }
295
+
296
+ echo '</td></tr>';
297
+ }
298
+
299
+ echo '</table></div>';
300
+
301
+ }
302
+ ?>
303
+ <p class="submit">
304
+ <input type="submit" class="button-primary" value="<?php _e( 'Save Changes', 'download_monitor' ); ?>" />
305
+ </p>
306
+ </form>
307
+ </div>
308
+ <?php
309
+
310
+ $download_monitor->add_inline_js("
311
+ jQuery('.nav-tab-wrapper a').click(function() {
312
+ jQuery('.settings_panel').hide();
313
+ jQuery('.nav-tab-active').removeClass('nav-tab-active');
314
+ jQuery( jQuery(this).attr('href') ).show();
315
+ jQuery(this).addClass('nav-tab-active');
316
+ return false;
317
+ });
318
+
319
+ jQuery('.nav-tab-wrapper a:first').click();
320
+ ");
321
+ }
322
+
323
+ /**
324
+ * log_viewer function.
325
+ *
326
+ * @access public
327
+ * @return void
328
+ */
329
+ function log_viewer() {
330
+ if ( ! class_exists( 'WP_List_Table' ) )
331
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
332
+
333
+ require_once( 'class-dlm-logging-list-table.php' );
334
+
335
+ $DLM_Logging_List_Table = new DLM_Logging_List_Table();
336
+ $DLM_Logging_List_Table->prepare_items();
337
+ ?>
338
+ <div class="wrap">
339
+ <div id="icon-edit" class="icon32 icon32-posts-dlm_download"><br/></div>
340
+
341
+ <h2><?php _e( 'Download Logs', 'download_monitor' ); ?> <a href="<?php echo admin_url( '?dlm_download_logs=true' ); ?>" class="add-new-h2"><?php _e( 'Export CSV', 'download_monitor' ); ?></a></h2><br/>
342
+ <form id="dlm_logs">
343
+ <?php $DLM_Logging_List_Table->display() ?>
344
+ </form>
345
+ </div>
346
+ <?php
347
+ }
348
+
349
+ /**
350
+ * export_logs function.
351
+ *
352
+ * @access public
353
+ * @return void
354
+ */
355
+ public function export_logs() {
356
+ global $wpdb;
357
+
358
+ if ( empty( $_GET['dlm_download_logs'] ) )
359
+ return;
360
+
361
+ $items = $wpdb->get_results(
362
+ "SELECT * FROM {$wpdb->download_log}
363
+ WHERE type = 'download'
364
+ ORDER BY download_date DESC"
365
+ );
366
+
367
+ $rows = array();
368
+ $row = array();
369
+ $row[] = __( 'Download ID', 'download_monitor' );
370
+ $row[] = __( 'Version ID', 'download_monitor' );
371
+ $row[] = __( 'User ID', 'download_monitor' );
372
+ $row[] = __( 'User IP', 'download_monitor' );
373
+ $row[] = __( 'User Agent', 'download_monitor' );
374
+ $row[] = __( 'Date', 'download_monitor' );
375
+ $row[] = __( 'Status', 'download_monitor' );
376
+ $rows[] = '"' . implode( '","', $row ) . '"';
377
+
378
+ if ( ! empty( $items ) ) {
379
+ foreach ( $items as $item ) {
380
+ $row = array();
381
+ $row[] = $item->download_id;
382
+ $row[] = $item->version_id;
383
+ $row[] = $item->user_id;
384
+ $row[] = $item->user_ip;
385
+ $row[] = $item->user_agent;
386
+ $row[] = $item->download_date;
387
+ $row[] = $item->download_status . ( $item->download_status_message ? ' - ' : '' ) . $item->download_status_message;
388
+ $rows[] = '"' . implode( '","', $row ) . '"';
389
+ }
390
+ }
391
+
392
+ $log = implode( "\n", $rows );
393
+
394
+ header( "Content-type: text/csv" );
395
+ header( "Content-Disposition: attachment; filename=download_log.csv" );
396
+ header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
397
+ header( "Content-Length: " . strlen( $log ) );
398
+ echo $log;
399
+ exit;
400
+ }
401
+
402
+ /**
403
+ * admin_dashboard function.
404
+ *
405
+ * @access public
406
+ * @return void
407
+ */
408
+ public function admin_dashboard() {
409
+ include_once( 'class-dlm-admin-dashboard.php' );
410
+ }
411
+ }
412
+
413
+ new DLM_Admin();
includes/admin/class-dlm-category-walker.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ /**
5
+ * DLM_Category_Walker class.
6
+ *
7
+ * @extends Walker
8
+ */
9
+ class DLM_Category_Walker extends Walker {
10
+
11
+ var $tree_type = 'category';
12
+ var $db_fields = array ('parent' => 'parent', 'id' => 'term_id', 'slug' => 'slug' );
13
+
14
+ /**
15
+ * @see Walker::start_el()
16
+ * @since 2.1.0
17
+ *
18
+ * @param string $output Passed by reference. Used to append additional content.
19
+ * @param object $category Category data object.
20
+ * @param int $depth Depth of category in reference to parents.
21
+ * @param array $args
22
+ */
23
+ function start_el( $output, $cat, $depth, $args ) {
24
+
25
+ if ( ! empty( $args['hierarchical'] ) )
26
+ $pad = str_repeat('&nbsp;', $depth * 3);
27
+ else
28
+ $pad = '';
29
+
30
+ $cat_name = apply_filters( 'list_product_cats', $cat->name, $cat );
31
+
32
+ $value = isset( $args['value'] ) && $args['value'] == 'id' ? $cat->term_id : $cat->slug;
33
+
34
+ $output .= "\t<option class=\"level-$depth\" value=\"" . $value . "\"";
35
+
36
+ if ( $value == $args['selected'] || ( is_array( $args['selected'] ) && in_array( $value, $args['selected'] ) ) )
37
+ $output .= ' selected="selected"';
38
+
39
+ $output .= '>';
40
+
41
+ $output .= $pad . __( $cat_name, 'download_monitor' );
42
+
43
+ if ( ! empty( $args['show_count'] ) )
44
+ $output .= '&nbsp;(' . $cat->count . ')';
45
+
46
+ $output .= "</option>\n";
47
+ }
48
+ }
includes/admin/class-dlm-logging-list-table.php ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * DLM_Logging_List_Table class.
4
+ *
5
+ * @extends WP_List_Table
6
+ */
7
+ class DLM_Logging_List_Table extends WP_List_Table {
8
+
9
+ /**
10
+ * __construct function.
11
+ *
12
+ * @access public
13
+ */
14
+ function __construct(){
15
+ global $status, $page;
16
+
17
+ parent::__construct( array(
18
+ 'singular' => 'log',
19
+ 'plural' => 'logs',
20
+ 'ajax' => false
21
+ ) );
22
+ }
23
+
24
+ /**
25
+ * column_default function.
26
+ *
27
+ * @access public
28
+ * @param mixed $log
29
+ * @param mixed $column_name
30
+ * @return void
31
+ */
32
+ function column_default( $log, $column_name ) {
33
+ switch( $column_name ) {
34
+ case 'status' :
35
+ switch ( $log->download_status ) {
36
+ case 'failed' :
37
+ $download_status = '<span class="failed" title="' . esc_attr( $log->download_status_message ) . '">&#10082;</span>';
38
+ break;
39
+ case 'redirected' :
40
+ $download_status = '<span class="redirected" title="' . esc_attr( $log->download_status_message ) . '">&#10140;</span>';
41
+ break;
42
+ default :
43
+ $download_status = '<span class="completed" title="' . __( 'Download Complete', 'download_monitor' ) . '">&#10004;</span>';
44
+ break;
45
+ }
46
+
47
+ return $download_status;
48
+ break;
49
+ case 'date' :
50
+ return '<time title="' . date_i18n( get_option( 'date_format' ) . ' @ ' . get_option( 'time_format' ), strtotime( $log->download_date ) ) . '"">' . sprintf( __( '%s ago', 'download_monitor' ), human_time_diff( strtotime( $log->download_date ) ) ) . '</time>';
51
+ break;
52
+ case 'download' :
53
+ $download = new DLM_Download( $log->download_id );
54
+ $download->set_version( $log->version_id );
55
+
56
+ if ( ! $download->exists() ) {
57
+ $download_string = sprintf( __( 'Download #%d (no longer exists)', 'download_monitor' ), $log->download_id );
58
+ } else {
59
+ $download_string = '<a href="' . admin_url( 'post.php?post=' . $download->id . '&action=edit' ) . '">';
60
+ $download_string .= '#' . $download->id . ' &ndash; ' . $download->get_the_title();
61
+ $download_string .= '</a>';
62
+ }
63
+
64
+ if ( $log->version )
65
+ $download_string .= ' (' . sprintf( __( 'v%s', 'download_monitor' ), $log->version ) . ')';
66
+
67
+ return $download_string;
68
+ break;
69
+ case 'file' :
70
+ $download = new DLM_Download( $log->download_id );
71
+ $download->set_version( $log->version_id );
72
+
73
+ if ( $download->exists() && $download->get_the_filename() )
74
+ $download_string = '<code>' . $download->get_the_filename() . '</code>';
75
+ else
76
+ $download_string = '&ndash;';
77
+
78
+ return $download_string;
79
+ break;
80
+ case 'user' :
81
+ if ( $log->user_id )
82
+ $user = get_user_by( 'id', $log->user_id );
83
+
84
+ if ( ! isset( $user ) || ! $user ) {
85
+ $user_string = __( 'Non-member', 'download_monitor' );
86
+ } else {
87
+ $user_string = '<a href="' . admin_url( 'user-edit.php?user_id=' . $user->ID ) . '">';
88
+ $user_string .= $user->user_login . ' &ndash; ';
89
+ $user_string .= '<a href="mailto:' . $user->user_email . '">';
90
+ $user_string .= $user->user_email;
91
+ $user_string .= '</a>';
92
+ }
93
+
94
+ return $user_string;
95
+ break;
96
+ case 'user_ip' :
97
+ return '<a href="http://whois.arin.net/rest/net/NET-' . $log->user_ip . '" target="_blank">' . $log->user_ip . '</a>';
98
+ break;
99
+ case 'user_ua' :
100
+ $ua = $this->uaparser->parse( $log->user_agent );
101
+
102
+ return $ua->toFullString;
103
+ break;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * get_columns function.
109
+ *
110
+ * @access public
111
+ * @return void
112
+ */
113
+ public function get_columns(){
114
+ $columns = array(
115
+ 'status' => '',
116
+ 'download' => __( 'Download', 'download_monitor' ),
117
+ 'file' => __( 'File', 'download_monitor' ),
118
+ 'user' => __( 'User', 'download_monitor' ),
119
+ 'user_ip' => __( 'IP Address', 'download_monitor' ),
120
+ 'user_ua' => __( 'User Agent', 'download_monitor' ),
121
+ 'date' => __( 'Date', 'download_monitor' ),
122
+ );
123
+ return $columns;
124
+ }
125
+
126
+ /**
127
+ * prepare_items function.
128
+ *
129
+ * @access public
130
+ * @return void
131
+ */
132
+ function prepare_items() {
133
+ global $wpdb;
134
+
135
+ $per_page = 40;
136
+ $current_page = $this->get_pagenum();
137
+
138
+ // Init headers
139
+ $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
140
+
141
+ // Total Count of Logs
142
+ $total_items = $wpdb->get_var(
143
+ "SELECT COUNT( ID ) FROM {$wpdb->download_log} WHERE type = 'download'"
144
+ );
145
+
146
+ // Get Logs
147
+ $this->items = $wpdb->get_results(
148
+ $wpdb->prepare(
149
+ "SELECT * FROM {$wpdb->download_log}
150
+ WHERE type = 'download'
151
+ ORDER BY download_date DESC
152
+ LIMIT %d, %d",
153
+ ( $current_page - 1 ) * $per_page,
154
+ $per_page
155
+ )
156
+ );
157
+
158
+ // Pagination
159
+ $this->set_pagination_args( array(
160
+ 'total_items' => $total_items,
161
+ 'per_page' => $per_page,
162
+ 'total_pages' => ceil( $total_items / $per_page )
163
+ ) );
164
+
165
+ // Parser
166
+ if ( ! class_exists( 'UAParser' ) )
167
+ require_once( "uaparser/uaparser.php" );
168
+
169
+ $this->uaparser = new UAParser;
170
+ }
171
+ }
includes/admin/html-downloadable-file-version.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="dlm-metabox closed downloadable_file" data-file="<?php echo $file_id; ?>">
2
+ <h3>
3
+ <button type="button" class="remove_file button"><?php _e( 'Remove', 'download_monitor' ); ?></button>
4
+ <div class="handlediv" title="<?php _e( 'Click to toggle', 'download_monitor' ); ?>"></div>
5
+ <strong>#<?php echo $file_id; ?> &mdash; <?php echo sprintf( __( 'Version <span class="version">%s</span> (%s)', 'download_monitor' ), ( $file_version ) ? $file_version : __('n/a', 'download_monitor'), date_i18n( get_option( 'date_format' ), strtotime( $file_post_date ) ) ); ?> &mdash; <?php echo sprintf( _n('Downloaded %s time', 'Downloaded %s times', $file_download_count, 'download_monitor'), $file_download_count ); ?></strong>
6
+ <input type="hidden" name="downloadable_file_id[<?php echo $i; ?>]" value="<?php echo $file_id; ?>" />
7
+ <input type="hidden" class="file_menu_order" name="downloadable_file_menu_order[<?php echo $i; ?>]" value="<?php echo $i; ?>" />
8
+ </h3>
9
+ <table cellpadding="0" cellspacing="0" class="dlm-metabox-content">
10
+ <tbody>
11
+ <tr>
12
+ <td width="1%">
13
+ <label><?php _e( 'Version', 'download_monitor' ); ?>:</label>
14
+ <input type="text" class="short" name="downloadable_file_version[<?php echo $i; ?>]" placeholder="<?php _e( 'n/a', 'download_monitor' ); ?>" value="<?php echo $file_version; ?>" />
15
+ </td>
16
+ <td rowspan="3">
17
+
18
+ <label><?php _e( 'File URL(s)', 'download_monitor' ); ?>:</label>
19
+ <textarea name="downloadable_file_urls[<?php echo $i; ?>]" wrap="off" class="downloadable_file_urls" cols="5" rows="5" placeholder="<?php _e( 'Enter one file path/URL per line - multiple files will be used as mirrors (chosen at random).', 'download_monitor' ); ?>"><?php echo esc_textarea( implode( "\n", $file_urls ) ); ?></textarea>
20
+ <p>
21
+ <a href="#" class="button dlm_upload_file" data-choose="<?php _e( 'Choose a file', 'woocommerce' ); ?>" data-update="<?php _e( 'Insert file URL', 'woocommerce' ); ?>"><?php _e( 'Upload file', 'download_monitor' ); ?></a>
22
+ <a href="#" class="button browse_for_file"><?php _e( 'Browse for file', 'download_monitor' ); ?></a>
23
+ </p>
24
+
25
+ </td>
26
+ </tr>
27
+ <tr>
28
+ <td>
29
+ <label><?php _e( 'Download count', 'download_monitor' ); ?>:</label>
30
+ <input type="text" class="short" name="downloadable_file_download_count[<?php echo $i; ?>]" placeholder="<?php echo $file_download_count; ?>" />
31
+ </td>
32
+ </tr>
33
+ <tr>
34
+ <td>
35
+ <label><?php _e('File Date', 'download_monitor'); ?>:</label>
36
+ <input type="text" class="date-picker-field" name="downloadable_file_date[<?php echo $i; ?>]" maxlength="10" value="<?php echo date('Y-m-d', strtotime( $file_post_date ) ); ?>" /> @ <input type="text" class="hour" placeholder="<?php _e('h', 'download_monitor') ?>" name="downloadable_file_date_hour[<?php echo $i; ?>]" maxlength="2" size="2" value="<?php echo date('H', strtotime( $file_post_date ) ); ?>" />:<input type="text" class="minute" placeholder="<?php _e('m', 'download_monitor') ?>" name="downloadable_file_date_minute[<?php echo $i; ?>]" maxlength="2" size="2" value="<?php echo date('i', strtotime( $file_post_date ) ); ?>" />
37
+ </td>
38
+ </tr>
39
+ </tbody>
40
+ </table>
41
+ </div>
includes/admin/uaparser/CHANGELOG ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT.
2
+ SUPER MINOR REVISION NUMBERS WON'T SHOW UP IN GITHUB BUT IT'LL HELP
3
+ ME ORGANIZE WHEN I MADE A SET OF FIXES
4
+
5
+ UAP-2.1.1
6
+ - FIX: making sure patch minor is being populated correctly when showing a mismatch
7
+
8
+ UAP-2.1.0
9
+ - ADD: support for custom regexes.json files (via @pravindahal)
10
+ - FIX: formerly private vars/functions are now protected (via @pravindahal)
11
+ - FIX: command line tool gets 'pretty' option for PHP 5.4 users (via @skyzyx)
12
+ - FIX: refactored the regexes.yaml test suite
13
+ - FIX: now check to see if allow_url_fopen is set to 'On' before trying to download the YAML file from the command line
14
+ - THX: thanks to @pravindahal and @skyzyx for the pull requests
15
+
16
+ UAP-2.0.1
17
+ - FIX: renamed uaParser, osParser, & deviceParser to uaParse, osParse, & deviceParse to address a bug with uaParser being recognized as the contruct function for the overall lib
18
+ - FIX: updated the test lib so that device failures are properly formatted
19
+
20
+ UAP-2.0.0
21
+ - ADD: toString() converts the version bits and family into a simple string
22
+ - ADD: toVersionString() converts the version bits into a simple string
23
+ - ADD: toFullString() combines the UA and OS family and version bits
24
+ - ADD: "convert" flag for uaparser-cli.php
25
+ - ADD: "pull & save just regexes.yaml" flag for uaparser-cli.php
26
+ - FIX: library is now a dynamic class
27
+ - FIX: attributes are now nested & populated like the other ua-parser libraries (e.g. $result->family is now $result->ua->family)
28
+ - FIX: uaParser(), osParser(), and deviceParser() are now public functions
29
+ - FIX: saves regexes.yaml as JSON for better performance
30
+ - FIX: removed the __DIR__ "fix"
31
+ - FIX: Apache log parsing now returns results when UA or OS families are set to "Other" and the device is listed as a smartphone or generic feature phone
32
+ - FIX: all tabs are now spaces
33
+ - FIX: a UA is now run against all three parsers
34
+ - FIX: renamed $debug var to $log to better reflect what it does
35
+ - DEL: is* boolean attributes (e.g. isMobile) have been removed
36
+ - DEL: will no longer auto-parse $_SERVER['HTTP_USER_AGENT'] if available
37
+ - DEL: tests no longer run against pgts_browser_list.yaml
38
+ - THX: thanks to @rjd22 for the dynamic class code/fix
39
+
40
+ UAP-1.5.0
41
+ - ADD: command line interface is now in its own file (via @Synchro)
42
+ - ADD: command line utility now supports parsing an Apache log file & recording the results
43
+ - ADD: command line utility can now parse a supplied user-agent string and push out a simple list or JSON
44
+ - ADD: test suite that uses the ua-parser project's test resources
45
+ - FIX: numerous comment & spacing fixes (via @Synchro & @Krinkle)
46
+ - FIX: remove PHP4 version of spyc (via @Synchro)
47
+ - FIX: remove .svn dirs in spyc (via @lopezdonaque)
48
+ - FIX: notes that the PHP 5.2 fix really was for 5.1 (via @Synchro) (knew this, i was lazy)
49
+ - FIX: lib now returns an object no matter what. now matches other ua-parser libs (via @Krinkle)
50
+ - FIX: checks that $_SERVER attr is set before including it. should be better for command line use.
51
+ - FIX: family attr now properly set in an edge case
52
+ - FIX: if regexes.yaml picks up bad slashes the PHP lib will account for it (e.g. GoogleTV regex)
53
+ - THX: thanks to @Krinkle and @Synchro for the numerous fixes
54
+
55
+ UAP-1.4.5
56
+ - FIX: an embarrassing debug print survived the last edit
57
+ - THX: thanks to @memakeit for dropping the bug report
58
+
59
+ UAP-1.4.4
60
+ - FIX: made sure that the regex file is only loaded once if running the library multiple times. performance boost.
61
+ - FIX: added support for identifying various game devices as mobile devices
62
+ - THX: thanks to @rjd22 for pointing out the perf issue
63
+
64
+ UAP-1.4.3
65
+ - FIX: added support for patch & family attributes to sort of match the other libraries
66
+
67
+ UAP-1.4.2
68
+ - FIX: notice if regexes.yaml is missing parens (e.g. match $1) for device & os names
69
+
70
+ UAP-1.4.1
71
+ - FIX: notice when using UAParser from the command line
72
+
73
+ UAP-1.4.0
74
+ - ADD: silent mode for the UA::get() method
75
+ - ADD: nobackup mode for the UA::get() method
76
+ - ADD: example of how to do a redirect with ua-parser-php
77
+ - The following were changes to regexes.yaml:
78
+ ---- ADD: support for Google Earth browser
79
+ ---- ADD: another regex for Firefox Mobile
80
+ ---- ADD: support for Firefox Alpha builds
81
+ ---- ADD: support for Sogou Explorer
82
+ ---- ADD: support for the Raven for Mac browser
83
+ ---- ADD: support for WebKit Nightly builds (though slightly pointless)
84
+ ---- FIX: better pattern matching for the Pale Moon browser
85
+
86
+ UAP-v1.3.2
87
+ - FIX: addressed false "tablet" classification for opera mobile & mini on android
88
+ - The following were changes to regexes.yaml:
89
+ ---- ADD: support for Tizen Browser (aka SLP Browser) from Samsung
90
+ ---- FIX: support for a new look Polaris 8.0 user agent string
91
+ ---- FIX: modified where the Epiphany Browser check happens
92
+
93
+ UAP-v1.3.1
94
+ - FIX: now doing some sanity cleaning on the user agent strings
95
+ - FIX: added a smarter default if the user agent isn't recognized at all
96
+
97
+ UAP-v1.3.0
98
+ - FIX: now points to Tobie's ua-parser project for the latest greatest YAML file
99
+ - FIX: YAML file is now standardized as regexes.yaml instead of user_agents_regex.yaml
100
+ - FIX: minor NOTICE issues resolved for very select UAs
101
+
102
+ UAP-v1.2.2
103
+ - The following were changes to user_agents_regex.yaml:
104
+ ---- ADD: support for UC Browser
105
+
106
+ UAP-v1.2.1
107
+ - The following were changes to user_agents_regex.yaml:
108
+ ---- ADD: support for android 4 user agents that have a dash in them
109
+
110
+ UAP-v1.2.0
111
+ - FIX: should be compatible with PHP 5.2
112
+ - FIX: addressed STRICT mode errors
113
+ - FIX: addressed NOTICE for a missing variable
114
+ - FIX: if isTablet is set to true then isMobile is set to false (mobile to me means phone)
115
+ - THX: Thanks to Mike Bond of WVU Libraries for pointing out the 5.2 incompatibility
116
+
117
+ UAP-v1.1.0
118
+ - FIX: a number of fixes from bryan shelton
119
+ - The following were changes to user_agents_regex.yaml:
120
+ ---- ADD: support for Chrome Mobile
121
+
122
+ UAP-v1.0.0
123
+ - just because i don't expect to update this anytime soon and ppl report it's working
124
+
125
+ UAP-v0.3.1
126
+ - FIX: swapped nil for null in parse()
127
+ - FIX: smarter/saner defaults
128
+ - FIX: now using isset() for family_replacement
129
+ - THX: thanks to bryan shelton for these fixes
130
+
131
+ UAP-v0.3.0
132
+ - ADD: can now supply a specific UA to be checked
133
+ - ADD: if the UA contains 'tablet' isTablet is marked true
134
+ - ADD: for certain mobile OSs they report a desktop browser. marking them mobile now.
135
+ - FIX: tablet listing should now be better
136
+ - FIX: the list of mobile browsers was updated
137
+ - FIX: made sure that certain checks won't fail as "false" if a version number was a 0
138
+ - FIX: for the device check, if it returns spider as a result it no longer marks it as mobile
139
+ - FIX: added more mobile browsers to that specific check
140
+ - The following were changes to user_agents_regex.yaml:
141
+ ---- ADD: symphony, minimo, teleca, semc, up.browser, bunjaloo, jasmine, & brew browsers supported
142
+ ---- ADD: windows mobile 6.5 os support
143
+ ---- ADD: amoi, asus, bird, dell, docomo, huawei, i-mate, kyocera, lenovo, lg, microsoft kind,
144
+ motorola, philips, samsung, softbank, & sony ericsson device checks
145
+ ---- FIX: mobile firefox, opera mobile & mini, obigo, polaris, nokiabrowser, ie mobile,
146
+ android, & mobile safari browser checks
147
+ ---- FIX: iOS support
148
+ ---- FIX: htc, android, palm/hp, kindle, ios, generic feature phones & spider device checks
149
+
150
+ UAP-v0.2.0
151
+ - ADD: added isMobile support
152
+ - ADD: added isTablet support
153
+ - ADD: added isComputer support
154
+ - ADD: added isSpider support
155
+
156
+ UAP-v0.1.0
157
+ - The following were changes to user_agents_regex.yaml:
158
+ ---- expanded support for Symbia & Nokia Devices,
159
+ ---- cleaned up some slashies,
160
+ ---- added Mobile Safari as the family replacement for iOS devices,
161
+ ---- better support for longer HTC device names
162
+ ---- added feature phones to the device check
163
+ ---- added generic smartphones to the device check
164
+ ---- added AvantGo to the ua check
165
+ ---- tweaked a lot of the nokia checks
166
+ ---- added kindle support to the device section
167
+ ---- added a generic catch for android devices.
168
+ ---- added support for blackberry devices
169
+ ---- changed the blackberry family to 'blackberry webkit' when it's a webkit browser
includes/admin/uaparser/LICENSE ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2011-2012 Dave Olsen, http://dmolsen.com
2
+ Licensed under the MIT license
3
+
4
+ ua-parser-php is a PHP-based pseudo-port of the ua-parser project. Learn more about the ua-parser project at:
5
+
6
+ http://code.google.com/p/ua-parser/
7
+
8
+ The user agents data from the ua-parser project is licensed under the Apache license.
9
+ spyc-0.5, for loading the YAML, is licensed under the MIT license.
10
+ The initial list of generic feature phones & smartphones came from Mobile Web OSP under the MIT license
11
+ The initial list of spiders was taken from Yiibu's profile project under the MIT license.
includes/admin/uaparser/README.md ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ua-parser PHP Library #
2
+
3
+ This is the PHP library for the [ua-parser](https://github.com/tobie/ua-parser) project.
4
+
5
+ ## v2.0 Changes ##
6
+
7
+ v2.0 of the PHP library, released in December 2012, marked a huge transition from the previous pseudo-port of `ua-parser` to a true port that matches up well with the other libraries in the `ua-parser` repo. The primary changes:
8
+
9
+ * the `UAParser` class is now dynamic
10
+ * properties are nested _(e.g. $result->family is now $result->ua->family)_
11
+ * a user agent string is now required when using `parse()`. the auto-magical "use the server provided UA" is no longer supported.
12
+ * `uaParse()`, `osParse()`, and `deviceParse()` are public and can be used to just return those select bits for a given user agent string.
13
+ * the `is*` boolean properties _(e.g. isMobile)_ have been dropped. they now exist as part of the `ua-classifier` project.
14
+
15
+ Please refer to the `CHANGELOG` for the full list of changes.
16
+
17
+ ## Demo ##
18
+
19
+ You can [test the PHP library](http://uaparser.dmolsen.com/) with your browser.
20
+
21
+ ## Usage ##
22
+
23
+ Straightforward:
24
+
25
+ ```php
26
+ require("uaparser.php");
27
+
28
+ $ua = "Mozilla/5.0 (Macintosh; Intel Ma...";
29
+
30
+ $parser = new UAParser;
31
+ $result = $parser->parse($ua);
32
+
33
+ print $result->ua->family; // Safari
34
+ print $result->ua->major; // 6
35
+ print $result->ua->minor; // 0
36
+ print $result->ua->patch; // 2
37
+ print $result->ua->toString; // Safari 6.0.2
38
+ print $result->ua->toVersionString; // 6.0.2
39
+
40
+ print $result->os->family; // Mac OS X
41
+ print $result->os->major; // 10
42
+ print $result->os->minor; // 7
43
+ print $result->os->patch; // 5
44
+ print $result->os->patch_minor; // [null]
45
+ print $result->os->toString; // Mac OS X 10.7.5
46
+ print $result->os->toVersionString; // 10.7.5
47
+
48
+ print $result->device->family; // Other
49
+
50
+ print $result->toFullString; // Safari 6.0.2/Mac OS X 10.7.5
51
+ print $result->uaOriginal; // Mozilla/5.0 (Macintosh; Intel Ma...
52
+ ```
53
+
54
+ ## Using Your Own Custom regexes.json File ##
55
+
56
+ You can use your own `regexes.json` file if you've customized the official file. I *strongly* encourage you to push back any changes you may have so others can benefit. That said, to use your own do the following:
57
+
58
+ ```php
59
+ require("uaparser.php");
60
+
61
+ $parser = new UAParser("path/to/custom/regexes.json");
62
+ ```
63
+
64
+ ## Using ua-parser PHP Library from the Command Line ##
65
+
66
+ A command line utility is now included with the PHP library. The following commands are supported:
67
+
68
+ ### Get Usage Info
69
+
70
+ Provides simple usage information:
71
+
72
+ php uaparser-cli.php
73
+
74
+ ### Update the regexes.json File
75
+
76
+ Fetches an updated YAML file for `ua-parser` and overwrites the current JSON file. You can use the following as part of a cron job that runs nightly.
77
+
78
+ php uaparser-cli.php -g [-s] [-n]
79
+
80
+ By default is verbose. Use `-s` to turn that feature off. By default creates a back-up. Use `-n` to turn that feature off.
81
+
82
+ ### Convert an Existing regexes.yaml File to regexes.json
83
+
84
+ With the change to v2.0 you may have an existing and customized YAML file for `ua-parser`. Use the following to convert it to JSON.
85
+
86
+ php uaparser-cli.php -c [-s] [-n]
87
+
88
+ By default is verbose. Use `-s` to turn that feature off. By default creates a back-up. Use `-n` to turn that feature off.
89
+
90
+ ### Grab Just the Latest regexes.yaml File From the Repository
91
+
92
+ If you need to add a new UA it's easier to edit the original YAML and then convert it to JSON.
93
+
94
+ php uaparser-cli.php -y
95
+
96
+ Fetches an updated YAML file. *Warning:* This method overwrites any existing `regexes.yaml` file.
97
+
98
+ ### Parse an Apache Log File
99
+
100
+ Parses the supplied Apache log file to test UAParser.php. Saves the UA to a file when the UA or OS family aren't recognized or when the UA is listed as a generic smartphone or as a generic feature phone.
101
+
102
+ php uaparser-cli.php -l "/path/to/apache/logfile"
103
+
104
+
105
+ ### Parse a Single User Agent String
106
+
107
+ Parses a user agent string and dumps the results as a list.
108
+
109
+ php uaparser-cli.php [-p] [-j] "your user agent string"
110
+
111
+ Use the `-j` flag to print the result as JSON. Use the `-p` flag to pretty print the JSON result when using PHP 5.4+.
112
+
113
+ ## Credits ##
114
+
115
+ Thanks to the [original ua-parser team](http://code.google.com/p/ua-parser/people/list) for making the YAML file available for others to build upon.
116
+
117
+ Also, many thanks to the following major contributors to the PHP library:
118
+
119
+ * Bryan Shelton
120
+ * Michael Bond
121
+ * @rjd22
122
+ * Timo Tijhof
123
+ * Marcus Bointon
124
+ * Ryan Parman
125
+ * Pravin Dahal
includes/admin/uaparser/lib/json/JSON.php ADDED
@@ -0,0 +1,867 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+ /**
4
+ * Converts to and from JSON format.
5
+ *
6
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
7
+ * format. It is easy for humans to read and write. It is easy for machines
8
+ * to parse and generate. It is based on a subset of the JavaScript
9
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
10
+ * This feature can also be found in Python. JSON is a text format that is
11
+ * completely language independent but uses conventions that are familiar
12
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
13
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
14
+ * ideal data-interchange language.
15
+ *
16
+ * This package provides a simple encoder and decoder for JSON notation. It
17
+ * is intended for use with client-side Javascript applications that make
18
+ * use of HTTPRequest to perform server communication functions - data can
19
+ * be encoded into JSON notation for use in a client-side javascript, or
20
+ * decoded from incoming Javascript requests. JSON format is native to
21
+ * Javascript, and can be directly eval()'ed with no further parsing
22
+ * overhead
23
+ *
24
+ * All strings should be in ASCII or UTF-8 format!
25
+ *
26
+ * LICENSE: Redistribution and use in source and binary forms, with or
27
+ * without modification, are permitted provided that the following
28
+ * conditions are met: Redistributions of source code must retain the
29
+ * above copyright notice, this list of conditions and the following
30
+ * disclaimer. Redistributions in binary form must reproduce the above
31
+ * copyright notice, this list of conditions and the following disclaimer
32
+ * in the documentation and/or other materials provided with the
33
+ * distribution.
34
+ *
35
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
36
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
37
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
38
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
39
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
40
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
41
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
42
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
44
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
45
+ * DAMAGE.
46
+ *
47
+ * @category
48
+ * @package Services_JSON
49
+ * @author Michal Migurski <mike-json@teczno.com>
50
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
51
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
52
+ * @copyright 2005 Michal Migurski
53
+ * @version CVS: $Id: JSON.php 292911 2010-01-02 04:04:10Z alan_k $
54
+ * @license http://www.opensource.org/licenses/bsd-license.php
55
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
56
+ */
57
+
58
+ /**
59
+ * Marker constant for Services_JSON::decode(), used to flag stack state
60
+ */
61
+ define('SERVICES_JSON_SLICE', 1);
62
+
63
+ /**
64
+ * Marker constant for Services_JSON::decode(), used to flag stack state
65
+ */
66
+ define('SERVICES_JSON_IN_STR', 2);
67
+
68
+ /**
69
+ * Marker constant for Services_JSON::decode(), used to flag stack state
70
+ */
71
+ define('SERVICES_JSON_IN_ARR', 3);
72
+
73
+ /**
74
+ * Marker constant for Services_JSON::decode(), used to flag stack state
75
+ */
76
+ define('SERVICES_JSON_IN_OBJ', 4);
77
+
78
+ /**
79
+ * Marker constant for Services_JSON::decode(), used to flag stack state
80
+ */
81
+ define('SERVICES_JSON_IN_CMT', 5);
82
+
83
+ /**
84
+ * Behavior switch for Services_JSON::decode()
85
+ */
86
+ define('SERVICES_JSON_LOOSE_TYPE', 16);
87
+
88
+ /**
89
+ * Behavior switch for Services_JSON::decode()
90
+ */
91
+ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
92
+
93
+ /**
94
+ * Converts to and from JSON format.
95
+ *
96
+ * Brief example of use:
97
+ *
98
+ * <code>
99
+ * // create a new instance of Services_JSON
100
+ * $json = new Services_JSON();
101
+ *
102
+ * // convert a complexe value to JSON notation, and send it to the browser
103
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
104
+ * $output = $json->encode($value);
105
+ *
106
+ * print($output);
107
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
108
+ *
109
+ * // accept incoming POST data, assumed to be in JSON notation
110
+ * $input = file_get_contents('php://input', 1000000);
111
+ * $value = $json->decode($input);
112
+ * </code>
113
+ */
114
+ class Services_JSON
115
+ {
116
+ /**
117
+ * constructs a new JSON instance
118
+ *
119
+ * @param int $use object behavior flags; combine with boolean-OR
120
+ *
121
+ * possible values:
122
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
123
+ * "{...}" syntax creates associative arrays
124
+ * instead of objects in decode().
125
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
126
+ * Values which can't be encoded (e.g. resources)
127
+ * appear as NULL instead of throwing errors.
128
+ * By default, a deeply-nested resource will
129
+ * bubble up with an error, so all return values
130
+ * from encode() should be checked with isError()
131
+ */
132
+ function Services_JSON($use = 0)
133
+ {
134
+ $this->use = $use;
135
+ }
136
+
137
+ /**
138
+ * convert a string from one UTF-16 char to one UTF-8 char
139
+ *
140
+ * Normally should be handled by mb_convert_encoding, but
141
+ * provides a slower PHP-only method for installations
142
+ * that lack the multibye string extension.
143
+ *
144
+ * @param string $utf16 UTF-16 character
145
+ * @return string UTF-8 character
146
+ * @access private
147
+ */
148
+ function utf162utf8($utf16)
149
+ {
150
+ // oh please oh please oh please oh please oh please
151
+ if(function_exists('mb_convert_encoding')) {
152
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
153
+ }
154
+
155
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
156
+
157
+ switch(true) {
158
+ case ((0x7F & $bytes) == $bytes):
159
+ // this case should never be reached, because we are in ASCII range
160
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
161
+ return chr(0x7F & $bytes);
162
+
163
+ case (0x07FF & $bytes) == $bytes:
164
+ // return a 2-byte UTF-8 character
165
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
166
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
167
+ . chr(0x80 | ($bytes & 0x3F));
168
+
169
+ case (0xFFFF & $bytes) == $bytes:
170
+ // return a 3-byte UTF-8 character
171
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
172
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
173
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
174
+ . chr(0x80 | ($bytes & 0x3F));
175
+ }
176
+
177
+ // ignoring UTF-32 for now, sorry
178
+ return '';
179
+ }
180
+
181
+ /**
182
+ * convert a string from one UTF-8 char to one UTF-16 char
183
+ *
184
+ * Normally should be handled by mb_convert_encoding, but
185
+ * provides a slower PHP-only method for installations
186
+ * that lack the multibye string extension.
187
+ *
188
+ * @param string $utf8 UTF-8 character
189
+ * @return string UTF-16 character
190
+ * @access private
191
+ */
192
+ function utf82utf16($utf8)
193
+ {
194
+ // oh please oh please oh please oh please oh please
195
+ if(function_exists('mb_convert_encoding')) {
196
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
197
+ }
198
+
199
+ switch(strlen($utf8)) {
200
+ case 1:
201
+ // this case should never be reached, because we are in ASCII range
202
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
203
+ return $utf8;
204
+
205
+ case 2:
206
+ // return a UTF-16 character from a 2-byte UTF-8 char
207
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
208
+ return chr(0x07 & (ord($utf8{0}) >> 2))
209
+ . chr((0xC0 & (ord($utf8{0}) << 6))
210
+ | (0x3F & ord($utf8{1})));
211
+
212
+ case 3:
213
+ // return a UTF-16 character from a 3-byte UTF-8 char
214
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
215
+ return chr((0xF0 & (ord($utf8{0}) << 4))
216
+ | (0x0F & (ord($utf8{1}) >> 2)))
217
+ . chr((0xC0 & (ord($utf8{1}) << 6))
218
+ | (0x7F & ord($utf8{2})));
219
+ }
220
+
221
+ // ignoring UTF-32 for now, sorry
222
+ return '';
223
+ }
224
+
225
+ /**
226
+ * encodes an arbitrary variable into JSON format (and sends JSON Header)
227
+ *
228
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
229
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
230
+ * if var is a strng, note that encode() always expects it
231
+ * to be in ASCII or UTF-8 format!
232
+ *
233
+ * @return mixed JSON string representation of input var or an error if a problem occurs
234
+ * @access public
235
+ */
236
+ function encode($var)
237
+ {
238
+ header('Content-type: application/json');
239
+ return $this->encodeUnsafe($var);
240
+ }
241
+ /**
242
+ * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow CSS!!!!)
243
+ *
244
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
245
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
246
+ * if var is a strng, note that encode() always expects it
247
+ * to be in ASCII or UTF-8 format!
248
+ *
249
+ * @return mixed JSON string representation of input var or an error if a problem occurs
250
+ * @access public
251
+ */
252
+ function encodeUnsafe($var)
253
+ {
254
+ // see bug #16908 - regarding numeric locale printing
255
+ $lc = setlocale(LC_NUMERIC, 0);
256
+ setlocale(LC_NUMERIC, 'C');
257
+ $ret = $this->_encode($var);
258
+ setlocale(LC_NUMERIC, $lc);
259
+ return $ret;
260
+
261
+ }
262
+ /**
263
+ * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
264
+ *
265
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
266
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
267
+ * if var is a strng, note that encode() always expects it
268
+ * to be in ASCII or UTF-8 format!
269
+ *
270
+ * @return mixed JSON string representation of input var or an error if a problem occurs
271
+ * @access public
272
+ */
273
+ function _encode($var)
274
+ {
275
+
276
+ switch (gettype($var)) {
277
+ case 'boolean':
278
+ return $var ? 'true' : 'false';
279
+
280
+ case 'NULL':
281
+ return 'null';
282
+
283
+ case 'integer':
284
+ return (int) $var;
285
+
286
+ case 'double':
287
+ case 'float':
288
+ return (float) $var;
289
+
290
+ case 'string':
291
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
292
+ $ascii = '';
293
+ $strlen_var = strlen($var);
294
+
295
+ /*
296
+ * Iterate over every character in the string,
297
+ * escaping with a slash or encoding to UTF-8 where necessary
298
+ */
299
+ for ($c = 0; $c < $strlen_var; ++$c) {
300
+
301
+ $ord_var_c = ord($var{$c});
302
+
303
+ switch (true) {
304
+ case $ord_var_c == 0x08:
305
+ $ascii .= '\b';
306
+ break;
307
+ case $ord_var_c == 0x09:
308
+ $ascii .= '\t';
309
+ break;
310
+ case $ord_var_c == 0x0A:
311
+ $ascii .= '\n';
312
+ break;
313
+ case $ord_var_c == 0x0C:
314
+ $ascii .= '\f';
315
+ break;
316
+ case $ord_var_c == 0x0D:
317
+ $ascii .= '\r';
318
+ break;
319
+
320
+ case $ord_var_c == 0x22:
321
+ case $ord_var_c == 0x2F:
322
+ case $ord_var_c == 0x5C:
323
+ // double quote, slash, slosh
324
+ $ascii .= '\\'.$var{$c};
325
+ break;
326
+
327
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
328
+ // characters U-00000000 - U-0000007F (same as ASCII)
329
+ $ascii .= $var{$c};
330
+ break;
331
+
332
+ case (($ord_var_c & 0xE0) == 0xC0):
333
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
334
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
335
+ if ($c+1 >= $strlen_var) {
336
+ $c += 1;
337
+ $ascii .= '?';
338
+ break;
339
+ }
340
+
341
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
342
+ $c += 1;
343
+ $utf16 = $this->utf82utf16($char);
344
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
345
+ break;
346
+
347
+ case (($ord_var_c & 0xF0) == 0xE0):
348
+ if ($c+2 >= $strlen_var) {
349
+ $c += 2;
350
+ $ascii .= '?';
351
+ break;
352
+ }
353
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
354
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
355
+ $char = pack('C*', $ord_var_c,
356
+ @ord($var{$c + 1}),
357
+ @ord($var{$c + 2}));
358
+ $c += 2;
359
+ $utf16 = $this->utf82utf16($char);
360
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
361
+ break;
362
+
363
+ case (($ord_var_c & 0xF8) == 0xF0):
364
+ if ($c+3 >= $strlen_var) {
365
+ $c += 3;
366
+ $ascii .= '?';
367
+ break;
368
+ }
369
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
370
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
371
+ $char = pack('C*', $ord_var_c,
372
+ ord($var{$c + 1}),
373
+ ord($var{$c + 2}),
374
+ ord($var{$c + 3}));
375
+ $c += 3;
376
+ $utf16 = $this->utf82utf16($char);
377
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
378
+ break;
379
+
380
+ case (($ord_var_c & 0xFC) == 0xF8):
381
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
382
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
383
+ if ($c+4 >= $strlen_var) {
384
+ $c += 4;
385
+ $ascii .= '?';
386
+ break;
387
+ }
388
+ $char = pack('C*', $ord_var_c,
389
+ ord($var{$c + 1}),
390
+ ord($var{$c + 2}),
391
+ ord($var{$c + 3}),
392
+ ord($var{$c + 4}));
393
+ $c += 4;
394
+ $utf16 = $this->utf82utf16($char);
395
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
396
+ break;
397
+
398
+ case (($ord_var_c & 0xFE) == 0xFC):
399
+ if ($c+5 >= $strlen_var) {
400
+ $c += 5;
401
+ $ascii .= '?';
402
+ break;
403
+ }
404
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
405
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
406
+ $char = pack('C*', $ord_var_c,
407
+ ord($var{$c + 1}),
408
+ ord($var{$c + 2}),
409
+ ord($var{$c + 3}),
410
+ ord($var{$c + 4}),
411
+ ord($var{$c + 5}));
412
+ $c += 5;
413
+ $utf16 = $this->utf82utf16($char);
414
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
415
+ break;
416
+ }
417
+ }
418
+ return '"'.$ascii.'"';
419
+
420
+ case 'array':
421
+ /*
422
+ * As per JSON spec if any array key is not an integer
423
+ * we must treat the the whole array as an object. We
424
+ * also try to catch a sparsely populated associative
425
+ * array with numeric keys here because some JS engines
426
+ * will create an array with empty indexes up to
427
+ * max_index which can cause memory issues and because
428
+ * the keys, which may be relevant, will be remapped
429
+ * otherwise.
430
+ *
431
+ * As per the ECMA and JSON specification an object may
432
+ * have any string as a property. Unfortunately due to
433
+ * a hole in the ECMA specification if the key is a
434
+ * ECMA reserved word or starts with a digit the
435
+ * parameter is only accessible using ECMAScript's
436
+ * bracket notation.
437
+ */
438
+
439
+ // treat as a JSON object
440
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
441
+ $properties = array_map(array($this, 'name_value'),
442
+ array_keys($var),
443
+ array_values($var));
444
+
445
+ foreach($properties as $property) {
446
+ if(Services_JSON::isError($property)) {
447
+ return $property;
448
+ }
449
+ }
450
+
451
+ return '{' . join(',', $properties) . '}';
452
+ }
453
+
454
+ // treat it like a regular array
455
+ $elements = array_map(array($this, '_encode'), $var);
456
+
457
+ foreach($elements as $element) {
458
+ if(Services_JSON::isError($element)) {
459
+ return $element;
460
+ }
461
+ }
462
+
463
+ return '[' . join(',', $elements) . ']';
464
+
465
+ case 'object':
466
+ $vars = get_object_vars($var);
467
+
468
+ $properties = array_map(array($this, 'name_value'),
469
+ array_keys($vars),
470
+ array_values($vars));
471
+
472
+ foreach($properties as $property) {
473
+ if(Services_JSON::isError($property)) {
474
+ return $property;
475
+ }
476
+ }
477
+
478
+ return '{' . join(',', $properties) . '}';
479
+
480
+ default:
481
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
482
+ ? 'null'
483
+ : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
484
+ }
485
+ }
486
+
487
+ /**
488
+ * array-walking function for use in generating JSON-formatted name-value pairs
489
+ *
490
+ * @param string $name name of key to use
491
+ * @param mixed $value reference to an array element to be encoded
492
+ *
493
+ * @return string JSON-formatted name-value pair, like '"name":value'
494
+ * @access private
495
+ */
496
+ function name_value($name, $value)
497
+ {
498
+ $encoded_value = $this->_encode($value);
499
+
500
+ if(Services_JSON::isError($encoded_value)) {
501
+ return $encoded_value;
502
+ }
503
+
504
+ return $this->_encode(strval($name)) . ':' . $encoded_value;
505
+ }
506
+
507
+ /**
508
+ * reduce a string by removing leading and trailing comments and whitespace
509
+ *
510
+ * @param $str string string value to strip of comments and whitespace
511
+ *
512
+ * @return string string value stripped of comments and whitespace
513
+ * @access private
514
+ */
515
+ function reduce_string($str)
516
+ {
517
+ $str = preg_replace(array(
518
+
519
+ // eliminate single line comments in '// ...' form
520
+ '#^\s*//(.+)$#m',
521
+
522
+ // eliminate multi-line comments in '/* ... */' form, at start of string
523
+ '#^\s*/\*(.+)\*/#Us',
524
+
525
+ // eliminate multi-line comments in '/* ... */' form, at end of string
526
+ '#/\*(.+)\*/\s*$#Us'
527
+
528
+ ), '', $str);
529
+
530
+ // eliminate extraneous space
531
+ return trim($str);
532
+ }
533
+
534
+ /**
535
+ * decodes a JSON string into appropriate variable
536
+ *
537
+ * @param string $str JSON-formatted string
538
+ *
539
+ * @return mixed number, boolean, string, array, or object
540
+ * corresponding to given JSON input string.
541
+ * See argument 1 to Services_JSON() above for object-output behavior.
542
+ * Note that decode() always returns strings
543
+ * in ASCII or UTF-8 format!
544
+ * @access public
545
+ */
546
+ function decode($str)
547
+ {
548
+ $str = $this->reduce_string($str);
549
+
550
+ switch (strtolower($str)) {
551
+ case 'true':
552
+ return true;
553
+
554
+ case 'false':
555
+ return false;
556
+
557
+ case 'null':
558
+ return null;
559
+
560
+ default:
561
+ $m = array();
562
+
563
+ if (is_numeric($str)) {
564
+ // Lookie-loo, it's a number
565
+
566
+ // This would work on its own, but I'm trying to be
567
+ // good about returning integers where appropriate:
568
+ // return (float)$str;
569
+
570
+ // Return float or int, as appropriate
571
+ return ((float)$str == (integer)$str)
572
+ ? (integer)$str
573
+ : (float)$str;
574
+
575
+ } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
576
+ // STRINGS RETURNED IN UTF-8 FORMAT
577
+ $delim = substr($str, 0, 1);
578
+ $chrs = substr($str, 1, -1);
579
+ $utf8 = '';
580
+ $strlen_chrs = strlen($chrs);
581
+
582
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
583
+
584
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
585
+ $ord_chrs_c = ord($chrs{$c});
586
+
587
+ switch (true) {
588
+ case $substr_chrs_c_2 == '\b':
589
+ $utf8 .= chr(0x08);
590
+ ++$c;
591
+ break;
592
+ case $substr_chrs_c_2 == '\t':
593
+ $utf8 .= chr(0x09);
594
+ ++$c;
595
+ break;
596
+ case $substr_chrs_c_2 == '\n':
597
+ $utf8 .= chr(0x0A);
598
+ ++$c;
599
+ break;
600
+ case $substr_chrs_c_2 == '\f':
601
+ $utf8 .= chr(0x0C);
602
+ ++$c;
603
+ break;
604
+ case $substr_chrs_c_2 == '\r':
605
+ $utf8 .= chr(0x0D);
606
+ ++$c;
607
+ break;
608
+
609
+ case $substr_chrs_c_2 == '\\"':
610
+ case $substr_chrs_c_2 == '\\\'':
611
+ case $substr_chrs_c_2 == '\\\\':
612
+ case $substr_chrs_c_2 == '\\/':
613
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
614
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
615
+ $utf8 .= $chrs{++$c};
616
+ }
617
+ break;
618
+
619
+ case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
620
+ // single, escaped unicode character
621
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
622
+ . chr(hexdec(substr($chrs, ($c + 4), 2)));
623
+ $utf8 .= $this->utf162utf8($utf16);
624
+ $c += 5;
625
+ break;
626
+
627
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
628
+ $utf8 .= $chrs{$c};
629
+ break;
630
+
631
+ case ($ord_chrs_c & 0xE0) == 0xC0:
632
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
633
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
634
+ $utf8 .= substr($chrs, $c, 2);
635
+ ++$c;
636
+ break;
637
+
638
+ case ($ord_chrs_c & 0xF0) == 0xE0:
639
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
640
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
641
+ $utf8 .= substr($chrs, $c, 3);
642
+ $c += 2;
643
+ break;
644
+
645
+ case ($ord_chrs_c & 0xF8) == 0xF0:
646
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
647
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
648
+ $utf8 .= substr($chrs, $c, 4);
649
+ $c += 3;
650
+ break;
651
+
652
+ case ($ord_chrs_c & 0xFC) == 0xF8:
653
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
654
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
655
+ $utf8 .= substr($chrs, $c, 5);
656
+ $c += 4;
657
+ break;
658
+
659
+ case ($ord_chrs_c & 0xFE) == 0xFC:
660
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
661
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
662
+ $utf8 .= substr($chrs, $c, 6);
663
+ $c += 5;
664
+ break;
665
+
666
+ }
667
+
668
+ }
669
+
670
+ return $utf8;
671
+
672
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
673
+ // array, or object notation
674
+
675
+ if ($str{0} == '[') {
676
+ $stk = array(SERVICES_JSON_IN_ARR);
677
+ $arr = array();
678
+ } else {
679
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
680
+ $stk = array(SERVICES_JSON_IN_OBJ);
681
+ $obj = array();
682
+ } else {
683
+ $stk = array(SERVICES_JSON_IN_OBJ);
684
+ $obj = new stdClass();
685
+ }
686
+ }
687
+
688
+ array_push($stk, array('what' => SERVICES_JSON_SLICE,
689
+ 'where' => 0,
690
+ 'delim' => false));
691
+
692
+ $chrs = substr($str, 1, -1);
693
+ $chrs = $this->reduce_string($chrs);
694
+
695
+ if ($chrs == '') {
696
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
697
+ return $arr;
698
+
699
+ } else {
700
+ return $obj;
701
+
702
+ }
703
+ }
704
+
705
+ //print("\nparsing {$chrs}\n");
706
+
707
+ $strlen_chrs = strlen($chrs);
708
+
709
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
710
+
711
+ $top = end($stk);
712
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
713
+
714
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
715
+ // found a comma that is not inside a string, array, etc.,
716
+ // OR we've reached the end of the character list
717
+ $slice = substr($chrs, $top['where'], ($c - $top['where']));
718
+ array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
719
+ //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
720
+
721
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
722
+ // we are in an array, so just push an element onto the stack
723
+ array_push($arr, $this->decode($slice));
724
+
725
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
726
+ // we are in an object, so figure
727
+ // out the property name and set an
728
+ // element in an associative array,
729
+ // for now
730
+ $parts = array();
731
+
732
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
733
+ // "name":value pair
734
+ $key = $this->decode($parts[1]);
735
+ $val = $this->decode($parts[2]);
736
+
737
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
738
+ $obj[$key] = $val;
739
+ } else {
740
+ $obj->$key = $val;
741
+ }
742
+ } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
743
+ // name:value pair, where name is unquoted
744
+ $key = $parts[1];
745
+ $val = $this->decode($parts[2]);
746
+
747
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
748
+ $obj[$key] = $val;
749
+ } else {
750
+ $obj->$key = $val;
751
+ }
752
+ }
753
+
754
+ }
755
+
756
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
757
+ // found a quote, and we are not inside a string
758
+ array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
759
+ //print("Found start of string at {$c}\n");
760
+
761
+ } elseif (($chrs{$c} == $top['delim']) &&
762
+ ($top['what'] == SERVICES_JSON_IN_STR) &&
763
+ ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
764
+ // found a quote, we're in a string, and it's not escaped
765
+ // we know that it's not escaped becase there is _not_ an
766
+ // odd number of backslashes at the end of the string so far
767
+ array_pop($stk);
768
+ //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
769
+
770
+ } elseif (($chrs{$c} == '[') &&
771
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
772
+ // found a left-bracket, and we are in an array, object, or slice
773
+ array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
774
+ //print("Found start of array at {$c}\n");
775
+
776
+ } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
777
+ // found a right-bracket, and we're in an array
778
+ array_pop($stk);
779
+ //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
780
+
781
+ } elseif (($chrs{$c} == '{') &&
782
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
783
+ // found a left-brace, and we are in an array, object, or slice
784
+ array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
785
+ //print("Found start of object at {$c}\n");
786
+
787
+ } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
788
+ // found a right-brace, and we're in an object
789
+ array_pop($stk);
790
+ //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
791
+
792
+ } elseif (($substr_chrs_c_2 == '/*') &&
793
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
794
+ // found a comment start, and we are in an array, object, or slice
795
+ array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
796
+ $c++;
797
+ //print("Found start of comment at {$c}\n");
798
+
799
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
800
+ // found a comment end, and we're in one now
801
+ array_pop($stk);
802
+ $c++;
803
+
804
+ for ($i = $top['where']; $i <= $c; ++$i)
805
+ $chrs = substr_replace($chrs, ' ', $i, 1);
806
+
807
+ //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
808
+
809
+ }
810
+
811
+ }
812
+
813
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
814
+ return (array)$arr;
815
+
816
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
817
+ return (array)$obj;
818
+
819
+ }
820
+
821
+ }
822
+ }
823
+ }
824
+
825
+ /**
826
+ * @todo Ultimately, this should just call PEAR::isError()
827
+ */
828
+ function isError($data, $code = null)
829
+ {
830
+ if (class_exists('pear')) {
831
+ return PEAR::isError($data, $code);
832
+ } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
833
+ is_subclass_of($data, 'services_json_error'))) {
834
+ return true;
835
+ }
836
+
837
+ return false;
838
+ }
839
+ }
840
+
841
+ if (class_exists('PEAR_Error')) {
842
+
843
+ class Services_JSON_Error extends PEAR_Error
844
+ {
845
+ function Services_JSON_Error($message = 'unknown error', $code = null,
846
+ $mode = null, $options = null, $userinfo = null)
847
+ {
848
+ parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
849
+ }
850
+ }
851
+
852
+ } else {
853
+
854
+ /**
855
+ * @todo Ultimately, this class shall be descended from PEAR_Error
856
+ */
857
+ class Services_JSON_Error
858
+ {
859
+ function Services_JSON_Error($message = 'unknown error', $code = null,
860
+ $mode = null, $options = null, $userinfo = null)
861
+ {
862
+
863
+ }
864
+ }
865
+
866
+ }
867
+
includes/admin/uaparser/lib/json/jsonwrapper.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once 'JSON.php';
4
+
5
+ function json_encode($arg) {
6
+ global $services_json;
7
+ if (!isset($services_json)) {
8
+ $services_json = new Services_JSON();
9
+ }
10
+ return $services_json->encode($arg);
11
+ }
12
+
13
+ function json_decode($arg) {
14
+ global $services_json;
15
+ if (!isset($services_json)) {
16
+ $services_json = new Services_JSON();
17
+ }
18
+ return $services_json->decode($arg);
19
+ }
20
+
21
+ ?>
includes/admin/uaparser/lib/spyc-0.5/COPYING ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License
2
+
3
+ Copyright (c) 2011 Vladimir Andersen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
includes/admin/uaparser/lib/spyc-0.5/README ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # S P Y C
3
+ # a simple php yaml class
4
+ #
5
+ # Load this README!
6
+ # >> $readme = Spyc::YAMLLoad('README');
7
+ #
8
+ --- %YAML:1.1
9
+ title: Spyc -- a Simple PHP YAML Class
10
+ version: 0.5
11
+ authors: [chris wanstrath (chris@ozmm.org), vlad andersen (vlad.andersen@gmail.com)]
12
+ websites: [http://www.yaml.org, http://spyc.sourceforge.net]
13
+ license: [MIT License, http://www.opensource.org/licenses/mit-license.php]
14
+ copyright: "(c) 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen"
15
+ tested on: [php 5.2.x]
16
+
17
+ installation: >
18
+ Copy spyc.php to a directory you can
19
+ access with your YAML-ready PHP script.
20
+
21
+ That's it!
22
+
23
+ about: >
24
+ From www.yaml.org:
25
+
26
+ "YAML(tm) (rhymes with 'camel') is a human-friendly, cross language,
27
+ Unicode based data serialization language designed around the common
28
+ native data structures of agile programming languages. It is broadly
29
+ useful for programming needs ranging from configuration files to
30
+ Internet messaging to object persistence to data auditing. Together
31
+ with the Unicode standard for characters, the YAML specification provides
32
+ all the information necessary to understand YAML Version 1.1 and to
33
+ creating programs that process YAML information.
34
+
35
+ YAML(tm) is a balance of the following design goals:
36
+ - YAML documents are very readable by humans.
37
+ - YAML interacts well with scripting languages.
38
+ - YAML uses host languages' native data structures.
39
+ - YAML has a consistent information model.
40
+ - YAML enables stream-based processing.
41
+ - YAML is expressive and extensible.
42
+ - YAML is easy to implement."
43
+
44
+ YAML makes a lot of sense. It's easy to use, easy to learn, and cool.
45
+ As the lucky stiff named why once said, "YAML is a beacon of light."
46
+
47
+ If you're new to YAML, may we suggest YAML In Five Minutes:
48
+ - http://yaml.kwiki.org/?YamlInFiveMinutes
49
+
50
+ If you don't have five minutes, realize that this README is a completely
51
+ valid YAML document. Dig in, load this or any YAML file into an array
52
+ with Spyc and see how easy it is to translate friendly text into usable
53
+ data.
54
+
55
+ The purpose of Spyc is to provide a pure PHP alternative to Syck, a
56
+ simple API for loading and dumping YAML documents, a YAML loader which
57
+ understands a usable subset of the YAML spec, and to further spread
58
+ the glory of YAML to the PHP masses.
59
+
60
+ If you're at all hesitant ("usable subset of YAML?!"), navigate
61
+ http://yaml.org/start.html. Spyc completely understands the YAML
62
+ document shown there, a document which has features way beyond the
63
+ scope of what normal config files might require. Try it for yourself,
64
+ and then start enjoying the peace of mind YAML brings to your life.
65
+
66
+ meat and a few potatoes:
67
+ - concept: Loading a YAML document into PHP
68
+ brief: >
69
+ $yaml will become an array of all the data in wicked.yaml
70
+ code: |
71
+
72
+ include('spyc.php');
73
+
74
+ $yaml = Spyc::YAMLLoad('wicked.yaml');
75
+
76
+ - concept: Loading a YAML string into PHP
77
+ brief: >
78
+ $array will look like this:
79
+ array('A YAML','document in a','string')
80
+ code: |
81
+
82
+ include('spyc.php');
83
+
84
+ $yaml = '- A YAML\n- document in a\n- string.';
85
+ $array = Spyc::YAMLLoad($yaml);
86
+
87
+ - concept: Dumping a PHP array to YAML
88
+ brief: >
89
+ $yaml will become a string of a YAML document created from
90
+ $array.
91
+ code: |
92
+
93
+ include('spyc.php');
94
+
95
+ $array['name'] = 'chris';
96
+ $array['sport'] = 'curbing';
97
+
98
+ $yaml = Spyc::YAMLDump($array);
99
+
100
+ prior art:
101
+ - who: [Brian Ingerson, Clark Evans, Oren Ben-Kiki]
102
+ why?: >
103
+ The YAML spec is really a piece of work, and these guys
104
+ did a great job on it. A simple and elegant language like
105
+ YAML was a long time coming and it's refreshing to know
106
+ such able minded individuals took the task to heart and
107
+ executed it with cunning and strength. In addition to
108
+ their various noteworthy contributions to YAML parsers
109
+ and related projects, YAML.pm's README is a treasure trove
110
+ of information for knowledge seekers. Thanks, guys.
111
+
112
+ - who: why the lucky stiff
113
+ why?: >
114
+ As the author of Syck, the code used in Ruby for the language's
115
+ YAML class and methods, why is indirectly (directly?) responsible
116
+ for my first exposure to YAML (as a config file in a Ruby web-app)
117
+ and the countless hours I spent playing with this sheik new data
118
+ format afterwards. Syck's README is a YAML file and thus the
119
+ inspiration for this file and, even, this very piece of software.
120
+
121
+ - who: Steve Howell
122
+ why?: >
123
+ Python's YAML implementation. PyYAML's README file is also YAML,
124
+ so it too inspired the YAML format of this README file.
125
+
126
+ - who: [Rasmus Lerdorf, Zeev Suraski, Andi Gutmans, et al]
127
+ why?: >
128
+ PHP is great at what it does best. It's also paid a lot of my bills.
129
+ Thanks.
130
+
131
+ bugs:
132
+ report: >
133
+ Please see Spyc's Sourceforge project page for information on reporting bugs.
134
+ speed: >
135
+ This implementation was not designed for speed. Rather, it
136
+ was designed for those who need a pure PHP implementation of
137
+ a YAML parser and who are not overly concerned with performance.
138
+ If you want speed, check out Syck.
139
+ depth: >
140
+ This parser is by no means a comprehensive YAML parser. For supported
141
+ features and future plans, check the website.
142
+ unicode: >
143
+ YAML is supposed to be unicode, but for now we're just using ASCII.
144
+ PHP has crappy unicode support but who knows what the future holds.
145
+
146
+ resources:
147
+ - http://www.yaml.org
148
+ - http://www.yaml.org/spec/
149
+ - http://yaml.kwiki.org/?YamlInFiveMinutes
150
+ - http://www.whytheluckystiff.net/syck/
151
+ - http://yaml4r.sourceforge.net/cookbook/
152
+
153
+ thanks:
154
+ - Adam Wood
155
+ - Daniel Ferreira
156
+ - Aaron Jensen
157
+ - Mike Thornton
158
+ - Fabien Potencier
159
+ - Mustafa Kumas
includes/admin/uaparser/lib/spyc-0.5/examples/yaml-dump.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ #
4
+ # S P Y C
5
+ # a simple php yaml class
6
+ #
7
+ # Feel free to dump an array to YAML, and then to load that YAML back into an
8
+ # array. This is a good way to test the limitations of the parser and maybe
9
+ # learn some basic YAML.
10
+ #
11
+
12
+ include('../spyc.php');
13
+
14
+ $array[] = 'Sequence item';
15
+ $array['The Key'] = 'Mapped value';
16
+ $array[] = array('A sequence','of a sequence');
17
+ $array[] = array('first' => 'A sequence','second' => 'of mapped values');
18
+ $array['Mapped'] = array('A sequence','which is mapped');
19
+ $array['A Note'] = 'What if your text is too long?';
20
+ $array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.';
21
+ $array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.';
22
+ $array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!";
23
+ $array['key:withcolon'] = "Should support this to";
24
+
25
+ $yaml = Spyc::YAMLDump($array,4,60);
includes/admin/uaparser/lib/spyc-0.5/examples/yaml-load.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ #
4
+ # S P Y C
5
+ # a simple php yaml class
6
+ #
7
+ # license: [MIT License, http://www.opensource.org/licenses/mit-license.php]
8
+ #
9
+
10
+ include('../spyc.php');
11
+
12
+ $array = Spyc::YAMLLoad('../spyc.yaml');
13
+
14
+ echo '<pre><a href="spyc.yaml">spyc.yaml</a> loaded into PHP:<br/>';
15
+ print_r($array);
16
+ echo '</pre>';
17
+
18
+
19
+ echo '<pre>YAML Data dumped back:<br/>';
20
+ echo Spyc::YAMLDump($array);
21
+ echo '</pre>';
includes/admin/uaparser/lib/spyc-0.5/spyc.php ADDED
@@ -0,0 +1,1046 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Spyc -- A Simple PHP YAML Class
4
+ * @version 0.5
5
+ * @author Vlad Andersen <vlad.andersen@gmail.com>
6
+ * @author Chris Wanstrath <chris@ozmm.org>
7
+ * @link http://code.google.com/p/spyc/
8
+ * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
9
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
10
+ * @package Spyc
11
+ */
12
+
13
+ if (!function_exists('spyc_load')) {
14
+ /**
15
+ * Parses YAML to array.
16
+ * @param string $string YAML string.
17
+ * @return array
18
+ */
19
+ function spyc_load ($string) {
20
+ return Spyc::YAMLLoadString($string);
21
+ }
22
+ }
23
+
24
+ if (!function_exists('spyc_load_file')) {
25
+ /**
26
+ * Parses YAML to array.
27
+ * @param string $file Path to YAML file.
28
+ * @return array
29
+ */
30
+ function spyc_load_file ($file) {
31
+ return Spyc::YAMLLoad($file);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * The Simple PHP YAML Class.
37
+ *
38
+ * This class can be used to read a YAML file and convert its contents
39
+ * into a PHP array. It currently supports a very limited subsection of
40
+ * the YAML spec.
41
+ *
42
+ * Usage:
43
+ * <code>
44
+ * $Spyc = new Spyc;
45
+ * $array = $Spyc->load($file);
46
+ * </code>
47
+ * or:
48
+ * <code>
49
+ * $array = Spyc::YAMLLoad($file);
50
+ * </code>
51
+ * or:
52
+ * <code>
53
+ * $array = spyc_load_file($file);
54
+ * </code>
55
+ * @package Spyc
56
+ */
57
+ class Spyc {
58
+
59
+ // SETTINGS
60
+
61
+ const REMPTY = "\0\0\0\0\0";
62
+
63
+ /**
64
+ * Setting this to true will force YAMLDump to enclose any string value in
65
+ * quotes. False by default.
66
+ *
67
+ * @var bool
68
+ */
69
+ public $setting_dump_force_quotes = false;
70
+
71
+ /**
72
+ * Setting this to true will forse YAMLLoad to use syck_load function when
73
+ * possible. False by default.
74
+ * @var bool
75
+ */
76
+ public $setting_use_syck_is_possible = false;
77
+
78
+
79
+
80
+ /**#@+
81
+ * @access private
82
+ * @var mixed
83
+ */
84
+ private $_dumpIndent;
85
+ private $_dumpWordWrap;
86
+ private $_containsGroupAnchor = false;
87
+ private $_containsGroupAlias = false;
88
+ private $path;
89
+ private $result;
90
+ private $LiteralPlaceHolder = '___YAML_Literal_Block___';
91
+ private $SavedGroups = array();
92
+ private $indent;
93
+ /**
94
+ * Path modifier that should be applied after adding current element.
95
+ * @var array
96
+ */
97
+ private $delayedPath = array();
98
+
99
+ /**#@+
100
+ * @access public
101
+ * @var mixed
102
+ */
103
+ public $_nodeId;
104
+
105
+ /**
106
+ * Load a valid YAML string to Spyc.
107
+ * @param string $input
108
+ * @return array
109
+ */
110
+ public function load ($input) {
111
+ return $this->__loadString($input);
112
+ }
113
+
114
+ /**
115
+ * Load a valid YAML file to Spyc.
116
+ * @param string $file
117
+ * @return array
118
+ */
119
+ public function loadFile ($file) {
120
+ return $this->__load($file);
121
+ }
122
+
123
+ /**
124
+ * Load YAML into a PHP array statically
125
+ *
126
+ * The load method, when supplied with a YAML stream (string or file),
127
+ * will do its best to convert YAML in a file into a PHP array. Pretty
128
+ * simple.
129
+ * Usage:
130
+ * <code>
131
+ * $array = Spyc::YAMLLoad('lucky.yaml');
132
+ * print_r($array);
133
+ * </code>
134
+ * @access public
135
+ * @return array
136
+ * @param string $input Path of YAML file or string containing YAML
137
+ */
138
+ public static function YAMLLoad($input) {
139
+ $Spyc = new Spyc;
140
+ return $Spyc->__load($input);
141
+ }
142
+
143
+ /**
144
+ * Load a string of YAML into a PHP array statically
145
+ *
146
+ * The load method, when supplied with a YAML string, will do its best
147
+ * to convert YAML in a string into a PHP array. Pretty simple.
148
+ *
149
+ * Note: use this function if you don't want files from the file system
150
+ * loaded and processed as YAML. This is of interest to people concerned
151
+ * about security whose input is from a string.
152
+ *
153
+ * Usage:
154
+ * <code>
155
+ * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
156
+ * print_r($array);
157
+ * </code>
158
+ * @access public
159
+ * @return array
160
+ * @param string $input String containing YAML
161
+ */
162
+ public static function YAMLLoadString($input) {
163
+ $Spyc = new Spyc;
164
+ return $Spyc->__loadString($input);
165
+ }
166
+
167
+ /**
168
+ * Dump YAML from PHP array statically
169
+ *
170
+ * The dump method, when supplied with an array, will do its best
171
+ * to convert the array into friendly YAML. Pretty simple. Feel free to
172
+ * save the returned string as nothing.yaml and pass it around.
173
+ *
174
+ * Oh, and you can decide how big the indent is and what the wordwrap
175
+ * for folding is. Pretty cool -- just pass in 'false' for either if
176
+ * you want to use the default.
177
+ *
178
+ * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
179
+ * you can turn off wordwrap by passing in 0.
180
+ *
181
+ * @access public
182
+ * @return string
183
+ * @param array $array PHP array
184
+ * @param int $indent Pass in false to use the default, which is 2
185
+ * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
186
+ */
187
+ public static function YAMLDump($array,$indent = false,$wordwrap = false) {
188
+ $spyc = new Spyc;
189
+ return $spyc->dump($array,$indent,$wordwrap);
190
+ }
191
+
192
+
193
+ /**
194
+ * Dump PHP array to YAML
195
+ *
196
+ * The dump method, when supplied with an array, will do its best
197
+ * to convert the array into friendly YAML. Pretty simple. Feel free to
198
+ * save the returned string as tasteful.yaml and pass it around.
199
+ *
200
+ * Oh, and you can decide how big the indent is and what the wordwrap
201
+ * for folding is. Pretty cool -- just pass in 'false' for either if
202
+ * you want to use the default.
203
+ *
204
+ * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
205
+ * you can turn off wordwrap by passing in 0.
206
+ *
207
+ * @access public
208
+ * @return string
209
+ * @param array $array PHP array
210
+ * @param int $indent Pass in false to use the default, which is 2
211
+ * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
212
+ */
213
+ public function dump($array,$indent = false,$wordwrap = false) {
214
+ // Dumps to some very clean YAML. We'll have to add some more features
215
+ // and options soon. And better support for folding.
216
+
217
+ // New features and options.
218
+ if ($indent === false or !is_numeric($indent)) {
219
+ $this->_dumpIndent = 2;
220
+ } else {
221
+ $this->_dumpIndent = $indent;
222
+ }
223
+
224
+ if ($wordwrap === false or !is_numeric($wordwrap)) {
225
+ $this->_dumpWordWrap = 40;
226
+ } else {
227
+ $this->_dumpWordWrap = $wordwrap;
228
+ }
229
+
230
+ // New YAML document
231
+ $string = "---\n";
232
+
233
+ // Start at the base of the array and move through it.
234
+ if ($array) {
235
+ $array = (array)$array;
236
+ $previous_key = -1;
237
+ foreach ($array as $key => $value) {
238
+ if (!isset($first_key)) $first_key = $key;
239
+ $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
240
+ $previous_key = $key;
241
+ }
242
+ }
243
+ return $string;
244
+ }
245
+
246
+ /**
247
+ * Attempts to convert a key / value array item to YAML
248
+ * @access private
249
+ * @return string
250
+ * @param $key The name of the key
251
+ * @param $value The value of the item
252
+ * @param $indent The indent of the current node
253
+ */
254
+ private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
255
+ if (is_array($value)) {
256
+ if (empty ($value))
257
+ return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
258
+ // It has children. What to do?
259
+ // Make it the right kind of item
260
+ $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
261
+ // Add the indent
262
+ $indent += $this->_dumpIndent;
263
+ // Yamlize the array
264
+ $string .= $this->_yamlizeArray($value,$indent);
265
+ } elseif (!is_array($value)) {
266
+ // It doesn't have children. Yip.
267
+ $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
268
+ }
269
+ return $string;
270
+ }
271
+
272
+ /**
273
+ * Attempts to convert an array to YAML
274
+ * @access private
275
+ * @return string
276
+ * @param $array The array you want to convert
277
+ * @param $indent The indent of the current level
278
+ */
279
+ private function _yamlizeArray($array,$indent) {
280
+ if (is_array($array)) {
281
+ $string = '';
282
+ $previous_key = -1;
283
+ foreach ($array as $key => $value) {
284
+ if (!isset($first_key)) $first_key = $key;
285
+ $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
286
+ $previous_key = $key;
287
+ }
288
+ return $string;
289
+ } else {
290
+ return false;
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Returns YAML from a key and a value
296
+ * @access private
297
+ * @return string
298
+ * @param $key The name of the key
299
+ * @param $value The value of the item
300
+ * @param $indent The indent of the current node
301
+ */
302
+ private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
303
+ // do some folding here, for blocks
304
+ if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
305
+ strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, ' ') !== false ||
306
+ strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
307
+ substr ($value, -1, 1) == ':')
308
+ ) {
309
+ $value = $this->_doLiteralBlock($value,$indent);
310
+ } else {
311
+ $value = $this->_doFolding($value,$indent);
312
+ }
313
+
314
+ if ($value === array()) $value = '[ ]';
315
+ if (in_array ($value, array ('true', 'TRUE', 'false', 'FALSE', 'y', 'Y', 'n', 'N', 'null', 'NULL'), true)) {
316
+ $value = $this->_doLiteralBlock($value,$indent);
317
+ }
318
+ if (trim ($value) != $value)
319
+ $value = $this->_doLiteralBlock($value,$indent);
320
+
321
+ if (is_bool($value)) {
322
+ $value = ($value) ? "true" : "false";
323
+ }
324
+
325
+ if ($value === null) $value = 'null';
326
+ if ($value === "'" . self::REMPTY . "'") $value = null;
327
+
328
+ $spaces = str_repeat(' ',$indent);
329
+
330
+ //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
331
+ if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
332
+ // It's a sequence
333
+ $string = $spaces.'- '.$value."\n";
334
+ } else {
335
+ // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
336
+ // It's mapped
337
+ if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
338
+ $string = rtrim ($spaces.$key.': '.$value)."\n";
339
+ }
340
+ return $string;
341
+ }
342
+
343
+ /**
344
+ * Creates a literal block for dumping
345
+ * @access private
346
+ * @return string
347
+ * @param $value
348
+ * @param $indent int The value of the indent
349
+ */
350
+ private function _doLiteralBlock($value,$indent) {
351
+ if ($value === "\n") return '\n';
352
+ if (strpos($value, "\n") === false && strpos($value, "'") === false) {
353
+ return sprintf ("'%s'", $value);
354
+ }
355
+ if (strpos($value, "\n") === false && strpos($value, '"') === false) {
356
+ return sprintf ('"%s"', $value);
357
+ }
358
+ $exploded = explode("\n",$value);
359
+ $newValue = '|';
360
+ $indent += $this->_dumpIndent;
361
+ $spaces = str_repeat(' ',$indent);
362
+ foreach ($exploded as $line) {
363
+ $newValue .= "\n" . $spaces . ($line);
364
+ }
365
+ return $newValue;
366
+ }
367
+
368
+ /**
369
+ * Folds a string of text, if necessary
370
+ * @access private
371
+ * @return string
372
+ * @param $value The string you wish to fold
373
+ */
374
+ private function _doFolding($value,$indent) {
375
+ // Don't do anything if wordwrap is set to 0
376
+
377
+ if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
378
+ $indent += $this->_dumpIndent;
379
+ $indent = str_repeat(' ',$indent);
380
+ $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
381
+ $value = ">\n".$indent.$wrapped;
382
+ } else {
383
+ if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
384
+ $value = '"' . $value . '"';
385
+ }
386
+
387
+
388
+ return $value;
389
+ }
390
+
391
+ // LOADING FUNCTIONS
392
+
393
+ private function __load($input) {
394
+ $Source = $this->loadFromSource($input);
395
+ return $this->loadWithSource($Source);
396
+ }
397
+
398
+ private function __loadString($input) {
399
+ $Source = $this->loadFromString($input);
400
+ return $this->loadWithSource($Source);
401
+ }
402
+
403
+ private function loadWithSource($Source) {
404
+ if (empty ($Source)) return array();
405
+ if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
406
+ $array = syck_load (implode ('', $Source));
407
+ return is_array($array) ? $array : array();
408
+ }
409
+
410
+ $this->path = array();
411
+ $this->result = array();
412
+
413
+ $cnt = count($Source);
414
+ for ($i = 0; $i < $cnt; $i++) {
415
+ $line = $Source[$i];
416
+
417
+ $this->indent = strlen($line) - strlen(ltrim($line));
418
+ $tempPath = $this->getParentPathByIndent($this->indent);
419
+ $line = self::stripIndent($line, $this->indent);
420
+ if (self::isComment($line)) continue;
421
+ if (self::isEmpty($line)) continue;
422
+ $this->path = $tempPath;
423
+
424
+ $literalBlockStyle = self::startsLiteralBlock($line);
425
+ if ($literalBlockStyle) {
426
+ $line = rtrim ($line, $literalBlockStyle . " \n");
427
+ $literalBlock = '';
428
+ $line .= $this->LiteralPlaceHolder;
429
+ $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
430
+ while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
431
+ $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
432
+ }
433
+ $i--;
434
+ }
435
+
436
+ while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
437
+ $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
438
+ }
439
+ $i--;
440
+
441
+
442
+
443
+ if (strpos ($line, '#')) {
444
+ if (strpos ($line, '"') === false && strpos ($line, "'") === false)
445
+ $line = preg_replace('/\s+#(.+)$/','',$line);
446
+ }
447
+
448
+ $lineArray = $this->_parseLine($line);
449
+
450
+ if ($literalBlockStyle)
451
+ $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
452
+
453
+ $this->addArray($lineArray, $this->indent);
454
+
455
+ foreach ($this->delayedPath as $indent => $delayedPath)
456
+ $this->path[$indent] = $delayedPath;
457
+
458
+ $this->delayedPath = array();
459
+
460
+ }
461
+ return $this->result;
462
+ }
463
+
464
+ private function loadFromSource ($input) {
465
+ if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
466
+ return file($input);
467
+
468
+ return $this->loadFromString($input);
469
+ }
470
+
471
+ private function loadFromString ($input) {
472
+ $lines = explode("\n",$input);
473
+ foreach ($lines as $k => $_) {
474
+ $lines[$k] = rtrim ($_, "\r");
475
+ }
476
+ return $lines;
477
+ }
478
+
479
+ /**
480
+ * Parses YAML code and returns an array for a node
481
+ * @access private
482
+ * @return array
483
+ * @param string $line A line from the YAML file
484
+ */
485
+ private function _parseLine($line) {
486
+ if (!$line) return array();
487
+ $line = trim($line);
488
+ if (!$line) return array();
489
+
490
+ $array = array();
491
+
492
+ $group = $this->nodeContainsGroup($line);
493
+ if ($group) {
494
+ $this->addGroup($line, $group);
495
+ $line = $this->stripGroup ($line, $group);
496
+ }
497
+
498
+ if ($this->startsMappedSequence($line))
499
+ return $this->returnMappedSequence($line);
500
+
501
+ if ($this->startsMappedValue($line))
502
+ return $this->returnMappedValue($line);
503
+
504
+ if ($this->isArrayElement($line))
505
+ return $this->returnArrayElement($line);
506
+
507
+ if ($this->isPlainArray($line))
508
+ return $this->returnPlainArray($line);
509
+
510
+
511
+ return $this->returnKeyValuePair($line);
512
+
513
+ }
514
+
515
+ /**
516
+ * Finds the type of the passed value, returns the value as the new type.
517
+ * @access private
518
+ * @param string $value
519
+ * @return mixed
520
+ */
521
+ private function _toType($value) {
522
+ if ($value === '') return null;
523
+ $first_character = $value[0];
524
+ $last_character = substr($value, -1, 1);
525
+
526
+ $is_quoted = false;
527
+ do {
528
+ if (!$value) break;
529
+ if ($first_character != '"' && $first_character != "'") break;
530
+ if ($last_character != '"' && $last_character != "'") break;
531
+ $is_quoted = true;
532
+ } while (0);
533
+
534
+ if ($is_quoted)
535
+ return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\'\'' => '\'', '\\\'' => '\''));
536
+
537
+ if (strpos($value, ' #') !== false && !$is_quoted)
538
+ $value = preg_replace('/\s+#(.+)$/','',$value);
539
+
540
+ if (!$is_quoted) $value = str_replace('\n', "\n", $value);
541
+
542
+ if ($first_character == '[' && $last_character == ']') {
543
+ // Take out strings sequences and mappings
544
+ $innerValue = trim(substr ($value, 1, -1));
545
+ if ($innerValue === '') return array();
546
+ $explode = $this->_inlineEscape($innerValue);
547
+ // Propagate value array
548
+ $value = array();
549
+ foreach ($explode as $v) {
550
+ $value[] = $this->_toType($v);
551
+ }
552
+ return $value;
553
+ }
554
+
555
+ if (strpos($value,': ')!==false && $first_character != '{') {
556
+ $array = explode(': ',$value);
557
+ $key = trim($array[0]);
558
+ array_shift($array);
559
+ $value = trim(implode(': ',$array));
560
+ $value = $this->_toType($value);
561
+ return array($key => $value);
562
+ }
563
+
564
+ if ($first_character == '{' && $last_character == '}') {
565
+ $innerValue = trim(substr ($value, 1, -1));
566
+ if ($innerValue === '') return array();
567
+ // Inline Mapping
568
+ // Take out strings sequences and mappings
569
+ $explode = $this->_inlineEscape($innerValue);
570
+ // Propagate value array
571
+ $array = array();
572
+ foreach ($explode as $v) {
573
+ $SubArr = $this->_toType($v);
574
+ if (empty($SubArr)) continue;
575
+ if (is_array ($SubArr)) {
576
+ $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
577
+ }
578
+ $array[] = $SubArr;
579
+ }
580
+ return $array;
581
+ }
582
+
583
+ if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
584
+ return null;
585
+ }
586
+
587
+ if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
588
+ $intvalue = (int)$value;
589
+ if ($intvalue != PHP_INT_MAX)
590
+ $value = $intvalue;
591
+ return $value;
592
+ }
593
+
594
+ if (in_array($value,
595
+ array('true', 'on', '+', 'yes', 'y', 'True', 'TRUE', 'On', 'ON', 'YES', 'Yes', 'Y'))) {
596
+ return true;
597
+ }
598
+
599
+ if (in_array(strtolower($value),
600
+ array('false', 'off', '-', 'no', 'n'))) {
601
+ return false;
602
+ }
603
+
604
+ if (is_numeric($value)) {
605
+ if ($value === '0') return 0;
606
+ if (rtrim ($value, 0) === $value)
607
+ $value = (float)$value;
608
+ return $value;
609
+ }
610
+
611
+ return $value;
612
+ }
613
+
614
+ /**
615
+ * Used in inlines to check for more inlines or quoted strings
616
+ * @access private
617
+ * @return array
618
+ */
619
+ private function _inlineEscape($inline) {
620
+ // There's gotta be a cleaner way to do this...
621
+ // While pure sequences seem to be nesting just fine,
622
+ // pure mappings and mappings with sequences inside can't go very
623
+ // deep. This needs to be fixed.
624
+
625
+ $seqs = array();
626
+ $maps = array();
627
+ $saved_strings = array();
628
+
629
+ // Check for strings
630
+ $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
631
+ if (preg_match_all($regex,$inline,$strings)) {
632
+ $saved_strings = $strings[0];
633
+ $inline = preg_replace($regex,'YAMLString',$inline);
634
+ }
635
+ unset($regex);
636
+
637
+ $i = 0;
638
+ do {
639
+
640
+ // Check for sequences
641
+ while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
642
+ $seqs[] = $matchseqs[0];
643
+ $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
644
+ }
645
+
646
+ // Check for mappings
647
+ while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
648
+ $maps[] = $matchmaps[0];
649
+ $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
650
+ }
651
+
652
+ if ($i++ >= 10) break;
653
+
654
+ } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
655
+
656
+ $explode = explode(', ',$inline);
657
+ $stringi = 0; $i = 0;
658
+
659
+ while (1) {
660
+
661
+ // Re-add the sequences
662
+ if (!empty($seqs)) {
663
+ foreach ($explode as $key => $value) {
664
+ if (strpos($value,'YAMLSeq') !== false) {
665
+ foreach ($seqs as $seqk => $seq) {
666
+ $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
667
+ $value = $explode[$key];
668
+ }
669
+ }
670
+ }
671
+ }
672
+
673
+ // Re-add the mappings
674
+ if (!empty($maps)) {
675
+ foreach ($explode as $key => $value) {
676
+ if (strpos($value,'YAMLMap') !== false) {
677
+ foreach ($maps as $mapk => $map) {
678
+ $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
679
+ $value = $explode[$key];
680
+ }
681
+ }
682
+ }
683
+ }
684
+
685
+
686
+ // Re-add the strings
687
+ if (!empty($saved_strings)) {
688
+ foreach ($explode as $key => $value) {
689
+ while (strpos($value,'YAMLString') !== false) {
690
+ $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
691
+ unset($saved_strings[$stringi]);
692
+ ++$stringi;
693
+ $value = $explode[$key];
694
+ }
695
+ }
696
+ }
697
+
698
+ $finished = true;
699
+ foreach ($explode as $key => $value) {
700
+ if (strpos($value,'YAMLSeq') !== false) {
701
+ $finished = false; break;
702
+ }
703
+ if (strpos($value,'YAMLMap') !== false) {
704
+ $finished = false; break;
705
+ }
706
+ if (strpos($value,'YAMLString') !== false) {
707
+ $finished = false; break;
708
+ }
709
+ }
710
+ if ($finished) break;
711
+
712
+ $i++;
713
+ if ($i > 10)
714
+ break; // Prevent infinite loops.
715
+ }
716
+
717
+ return $explode;
718
+ }
719
+
720
+ private function literalBlockContinues ($line, $lineIndent) {
721
+ if (!trim($line)) return true;
722
+ if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
723
+ return false;
724
+ }
725
+
726
+ private function referenceContentsByAlias ($alias) {
727
+ do {
728
+ if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
729
+ $groupPath = $this->SavedGroups[$alias];
730
+ $value = $this->result;
731
+ foreach ($groupPath as $k) {
732
+ $value = $value[$k];
733
+ }
734
+ } while (false);
735
+ return $value;
736
+ }
737
+
738
+ private function addArrayInline ($array, $indent) {
739
+ $CommonGroupPath = $this->path;
740
+ if (empty ($array)) return false;
741
+
742
+ foreach ($array as $k => $_) {
743
+ $this->addArray(array($k => $_), $indent);
744
+ $this->path = $CommonGroupPath;
745
+ }
746
+ return true;
747
+ }
748
+
749
+ private function addArray ($incoming_data, $incoming_indent) {
750
+
751
+ // print_r ($incoming_data);
752
+
753
+ if (count ($incoming_data) > 1)
754
+ return $this->addArrayInline ($incoming_data, $incoming_indent);
755
+
756
+ $key = key ($incoming_data);
757
+ $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
758
+ if ($key === '__!YAMLZero') $key = '0';
759
+
760
+ if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
761
+ if ($key || $key === '' || $key === '0') {
762
+ $this->result[$key] = $value;
763
+ } else {
764
+ $this->result[] = $value; end ($this->result); $key = key ($this->result);
765
+ }
766
+ $this->path[$incoming_indent] = $key;
767
+ return;
768
+ }
769
+
770
+
771
+
772
+ $history = array();
773
+ // Unfolding inner array tree.
774
+ $history[] = $_arr = $this->result;
775
+ foreach ($this->path as $k) {
776
+ $history[] = $_arr = $_arr[$k];
777
+ }
778
+
779
+ if ($this->_containsGroupAlias) {
780
+ $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
781
+ $this->_containsGroupAlias = false;
782
+ }
783
+
784
+
785
+ // Adding string or numeric key to the innermost level or $this->arr.
786
+ if (is_string($key) && $key == '<<') {
787
+ if (!is_array ($_arr)) { $_arr = array (); }
788
+
789
+ $_arr = array_merge ($_arr, $value);
790
+ } else if ($key || $key === '' || $key === '0') {
791
+ if (!is_array ($_arr))
792
+ $_arr = array ($key=>$value);
793
+ else
794
+ $_arr[$key] = $value;
795
+ } else {
796
+ if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
797
+ else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
798
+ }
799
+
800
+ $reverse_path = array_reverse($this->path);
801
+ $reverse_history = array_reverse ($history);
802
+ $reverse_history[0] = $_arr;
803
+ $cnt = count($reverse_history) - 1;
804
+ for ($i = 0; $i < $cnt; $i++) {
805
+ $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
806
+ }
807
+ $this->result = $reverse_history[$cnt];
808
+
809
+ $this->path[$incoming_indent] = $key;
810
+
811
+ if ($this->_containsGroupAnchor) {
812
+ $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
813
+ if (is_array ($value)) {
814
+ $k = key ($value);
815
+ if (!is_int ($k)) {
816
+ $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
817
+ }
818
+ }
819
+ $this->_containsGroupAnchor = false;
820
+ }
821
+
822
+ }
823
+
824
+ private static function startsLiteralBlock ($line) {
825
+ $lastChar = substr (trim($line), -1);
826
+ if ($lastChar != '>' && $lastChar != '|') return false;
827
+ if ($lastChar == '|') return $lastChar;
828
+ // HTML tags should not be counted as literal blocks.
829
+ if (preg_match ('#<.*?>$#', $line)) return false;
830
+ return $lastChar;
831
+ }
832
+
833
+ private static function greedilyNeedNextLine($line) {
834
+ $line = trim ($line);
835
+ if (!strlen($line)) return false;
836
+ if (substr ($line, -1, 1) == ']') return false;
837
+ if ($line[0] == '[') return true;
838
+ if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
839
+ return false;
840
+ }
841
+
842
+ private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
843
+ $line = self::stripIndent($line, $indent);
844
+ if ($literalBlockStyle !== '|') {
845
+ $line = self::stripIndent($line);
846
+ }
847
+ $line = rtrim ($line, "\r\n\t ") . "\n";
848
+ if ($literalBlockStyle == '|') {
849
+ return $literalBlock . $line;
850
+ }
851
+ if (strlen($line) == 0)
852
+ return rtrim($literalBlock, ' ') . "\n";
853
+ if ($line == "\n" && $literalBlockStyle == '>') {
854
+ return rtrim ($literalBlock, " \t") . "\n";
855
+ }
856
+ if ($line != "\n")
857
+ $line = trim ($line, "\r\n ") . " ";
858
+ return $literalBlock . $line;
859
+ }
860
+
861
+ function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
862
+ foreach ($lineArray as $k => $_) {
863
+ if (is_array($_))
864
+ $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
865
+ else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
866
+ $lineArray[$k] = rtrim ($literalBlock, " \r\n");
867
+ }
868
+ return $lineArray;
869
+ }
870
+
871
+ private static function stripIndent ($line, $indent = -1) {
872
+ if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
873
+ return substr ($line, $indent);
874
+ }
875
+
876
+ private function getParentPathByIndent ($indent) {
877
+ if ($indent == 0) return array();
878
+ $linePath = $this->path;
879
+ do {
880
+ end($linePath); $lastIndentInParentPath = key($linePath);
881
+ if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
882
+ } while ($indent <= $lastIndentInParentPath);
883
+ return $linePath;
884
+ }
885
+
886
+
887
+ private function clearBiggerPathValues ($indent) {
888
+
889
+
890
+ if ($indent == 0) $this->path = array();
891
+ if (empty ($this->path)) return true;
892
+
893
+ foreach ($this->path as $k => $_) {
894
+ if ($k > $indent) unset ($this->path[$k]);
895
+ }
896
+
897
+ return true;
898
+ }
899
+
900
+
901
+ private static function isComment ($line) {
902
+ if (!$line) return false;
903
+ if ($line[0] == '#') return true;
904
+ if (trim($line, " \r\n\t") == '---') return true;
905
+ return false;
906
+ }
907
+
908
+ private static function isEmpty ($line) {
909
+ return (trim ($line) === '');
910
+ }
911
+
912
+
913
+ private function isArrayElement ($line) {
914
+ if (!$line) return false;
915
+ if ($line[0] != '-') return false;
916
+ if (strlen ($line) > 3)
917
+ if (substr($line,0,3) == '---') return false;
918
+
919
+ return true;
920
+ }
921
+
922
+ private function isHashElement ($line) {
923
+ return strpos($line, ':');
924
+ }
925
+
926
+ private function isLiteral ($line) {
927
+ if ($this->isArrayElement($line)) return false;
928
+ if ($this->isHashElement($line)) return false;
929
+ return true;
930
+ }
931
+
932
+
933
+ private static function unquote ($value) {
934
+ if (!$value) return $value;
935
+ if (!is_string($value)) return $value;
936
+ if ($value[0] == '\'') return trim ($value, '\'');
937
+ if ($value[0] == '"') return trim ($value, '"');
938
+ return $value;
939
+ }
940
+
941
+ private function startsMappedSequence ($line) {
942
+ return ($line[0] == '-' && substr ($line, -1, 1) == ':');
943
+ }
944
+
945
+ private function returnMappedSequence ($line) {
946
+ $array = array();
947
+ $key = self::unquote(trim(substr($line,1,-1)));
948
+ $array[$key] = array();
949
+ $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
950
+ return array($array);
951
+ }
952
+
953
+ private function returnMappedValue ($line) {
954
+ $array = array();
955
+ $key = self::unquote (trim(substr($line,0,-1)));
956
+ $array[$key] = '';
957
+ return $array;
958
+ }
959
+
960
+ private function startsMappedValue ($line) {
961
+ return (substr ($line, -1, 1) == ':');
962
+ }
963
+
964
+ private function isPlainArray ($line) {
965
+ return ($line[0] == '[' && substr ($line, -1, 1) == ']');
966
+ }
967
+
968
+ private function returnPlainArray ($line) {
969
+ return $this->_toType($line);
970
+ }
971
+
972
+ private function returnKeyValuePair ($line) {
973
+ $array = array();
974
+ $key = '';
975
+ if (strpos ($line, ':')) {
976
+ // It's a key/value pair most likely
977
+ // If the key is in double quotes pull it out
978
+ if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
979
+ $value = trim(str_replace($matches[1],'',$line));
980
+ $key = $matches[2];
981
+ } else {
982
+ // Do some guesswork as to the key and the value
983
+ $explode = explode(':',$line);
984
+ $key = trim($explode[0]);
985
+ array_shift($explode);
986
+ $value = trim(implode(':',$explode));
987
+ }
988
+ // Set the type of the value. Int, string, etc
989
+ $value = $this->_toType($value);
990
+ if ($key === '0') $key = '__!YAMLZero';
991
+ $array[$key] = $value;
992
+ } else {
993
+ $array = array ($line);
994
+ }
995
+ return $array;
996
+
997
+ }
998
+
999
+
1000
+ private function returnArrayElement ($line) {
1001
+ if (strlen($line) <= 1) return array(array()); // Weird %)
1002
+ $array = array();
1003
+ $value = trim(substr($line,1));
1004
+ $value = $this->_toType($value);
1005
+ $array[] = $value;
1006
+ return $array;
1007
+ }
1008
+
1009
+
1010
+ private function nodeContainsGroup ($line) {
1011
+ $symbolsForReference = 'A-z0-9_\-';
1012
+ if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
1013
+ if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
1014
+ if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
1015
+ if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
1016
+ if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
1017
+ if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
1018
+ return false;
1019
+
1020
+ }
1021
+
1022
+ private function addGroup ($line, $group) {
1023
+ if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
1024
+ if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
1025
+ //print_r ($this->path);
1026
+ }
1027
+
1028
+ private function stripGroup ($line, $group) {
1029
+ $line = trim(str_replace($group, '', $line));
1030
+ return $line;
1031
+ }
1032
+ }
1033
+
1034
+ // Enable use of Spyc from command line
1035
+ // The syntax is the following: php spyc.php spyc.yaml
1036
+
1037
+ define ('SPYC_FROM_COMMAND_LINE', false);
1038
+
1039
+ do {
1040
+ if (!SPYC_FROM_COMMAND_LINE) break;
1041
+ if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
1042
+ if (empty ($_SERVER['PHP_SELF']) || $_SERVER['PHP_SELF'] != 'spyc.php') break;
1043
+ $file = $argv[1];
1044
+ printf ("Spyc loading file: %s\n", $file);
1045
+ print_r (spyc_load_file ($file));
1046
+ } while (0);
includes/admin/uaparser/lib/spyc-0.5/spyc.yaml ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # S P Y C
3
+ # a simple php yaml class
4
+ #
5
+ # authors: [vlad andersen (vlad.andersen@gmail.com), chris wanstrath (chris@ozmm.org)]
6
+ # websites: [http://www.yaml.org, http://spyc.sourceforge.net/]
7
+ # license: [MIT License, http://www.opensource.org/licenses/mit-license.php]
8
+ # copyright: (c) 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
9
+ #
10
+ # spyc.yml - A file containing the YAML that Spyc understands.
11
+
12
+ ---
13
+
14
+ # Mappings - with proper types
15
+ String: Anyone's name, really.
16
+ Int: 13
17
+ True: true
18
+ False: false
19
+ Zero: 0
20
+ Null: NULL
21
+ NotNull: 'null'
22
+ NotTrue: 'y'
23
+ NotBoolTrue: 'true'
24
+ NotInt: '5'
25
+ Float: 5.34
26
+ Negative: -90
27
+ SmallFloat: 0.7
28
+ NewLine: \n
29
+
30
+ # A sequence
31
+ - PHP Class
32
+ - Basic YAML Loader
33
+ - Very Basic YAML Dumper
34
+
35
+ # A sequence of a sequence
36
+ -
37
+ - YAML is so easy to learn.
38
+ - Your config files will never be the same.
39
+
40
+ # Sequence of mappings
41
+ -
42
+ cpu: 1.5ghz
43
+ ram: 1 gig
44
+ os : os x 10.4.1
45
+
46
+ # Mapped sequence
47
+ domains:
48
+ - yaml.org
49
+ - php.net
50
+
51
+ # A sequence like this.
52
+ - program: Adium
53
+ platform: OS X
54
+ type: Chat Client
55
+
56
+ # A folded block as a mapped value
57
+ no time: >
58
+ There isn't any time
59
+ for your tricks!
60
+
61
+ Do you understand?
62
+
63
+ # A literal block as a mapped value
64
+ some time: |
65
+ There is nothing but time
66
+ for your tricks.
67
+
68
+ # Crazy combinations
69
+ databases:
70
+ - name: spartan
71
+ notes:
72
+ - Needs to be backed up
73
+ - Needs to be normalized
74
+ type: mysql
75
+
76
+ # You can be a bit tricky
77
+ "if: you'd": like
78
+
79
+ # Inline sequences
80
+ - [One, Two, Three, Four]
81
+
82
+ # Nested Inline Sequences
83
+ - [One, [Two, And, Three], Four, Five]
84
+
85
+ # Nested Nested Inline Sequences
86
+ - [This, [Is, Getting, [Ridiculous, Guys]], Seriously, [Show, Mercy]]
87
+
88
+ # Inline mappings
89
+ - {name: chris, age: young, brand: lucky strike}
90
+
91
+ # Nested inline mappings
92
+ - {name: mark, age: older than chris, brand: [marlboro, lucky strike]}
93
+
94
+ # References -- they're shaky, but functional
95
+ dynamic languages: &DLANGS
96
+ - Perl
97
+ - Python
98
+ - PHP
99
+ - Ruby
100
+ compiled languages: &CLANGS
101
+ - C/C++
102
+ - Java
103
+ all languages:
104
+ - *DLANGS
105
+ - *CLANGS
106
+
107
+ # Added in .2.2: Escaped quotes
108
+ - you know, this shouldn't work. but it does.
109
+ - 'that''s my value.'
110
+ - 'again, that\'s my value.'
111
+ - "here's to \"quotes\", boss."
112
+
113
+ # added in .2.3
114
+ - {name: "Foo, Bar's", age: 20}
115
+
116
+ # Added in .2.4: bug [ 1418193 ] Quote Values in Nested Arrays
117
+ - [a, ['1', "2"], b]
118
+
119
+ # Added in .2.4: malformed YAML
120
+ all
121
+ javascripts: [dom1.js, dom.js]
122
+
123
+ # Added in .2
124
+ 1040: Ooo, a numeric key! # And working comments? Wow! Colons in comments: a menace (0.3).
125
+
126
+ hash_1: Hash #and a comment
127
+ hash_2: "Hash #and a comment"
128
+ "hash#3": "Hash (#) can appear in key too"
129
+
130
+ float_test: 1.0
131
+ float_test_with_quotes: '1.0'
132
+ float_inverse_test: 001
133
+
134
+ a_really_large_number: 115792089237316195423570985008687907853269984665640564039457584007913129639936 # 2^256
135
+
136
+ int array: [ 1, 2, 3 ]
137
+
138
+ array on several lines:
139
+ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
140
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]
141
+
142
+ morelesskey: "<value>"
143
+
144
+ array_of_zero: [0]
145
+ sophisticated_array_of_zero: {rx: {tx: [0]} }
146
+
147
+ switches:
148
+ - { row: 0, col: 0, func: {tx: [0, 1]} }
149
+
150
+ empty_sequence: [ ]
151
+ empty_hash: { }
152
+
153
+ special_characters: "[{]]{{]]"
154
+
155
+ asterisks: "*"
156
+
157
+ empty_key:
158
+ :
159
+ key: value
160
+
161
+ trailing_colon: "foo:"
162
+
163
+ multiline_items:
164
+ - type: SomeItem
165
+ values: [blah, blah, blah,
166
+ blah]
167
+ ints: [2, 54, 12,
168
+ 2143]
169
+
170
+ many_lines: |
171
+ A quick
172
+ fox
173
+
174
+
175
+ jumped
176
+ over
177
+
178
+
179
+
180
+
181
+
182
+ a lazy
183
+
184
+
185
+
186
+ dog
187
+
188
+
189
+ werte:
190
+ 1: nummer 1
191
+ 0: Stunde 0
192
+
193
+ noindent_records:
194
+ - record1: value1
195
+ - record2: value2
196
+
197
+ "a:1": [1000]
198
+ "a:2":
199
+ - 2000
200
+
201
+ # [Endloop]
202
+ endloop: |
203
+ Does this line in the end indeed make Spyc go to an infinite loop?
includes/admin/uaparser/lib/spyc-0.5/tests/DumpTest.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once ("../spyc.php");
4
+
5
+ class DumpTest extends PHPUnit_Framework_TestCase {
6
+
7
+ private $files_to_test = array();
8
+
9
+ public function setUp() {
10
+ $this->files_to_test = array ('../spyc.yaml', 'failing1.yaml', 'indent_1.yaml', 'quotes.yaml');
11
+ }
12
+
13
+ public function testDump() {
14
+ foreach ($this->files_to_test as $file) {
15
+ $yaml = spyc_load(file_get_contents($file));
16
+ $dump = Spyc::YAMLDump ($yaml);
17
+ $yaml_after_dump = Spyc::YAMLLoad ($dump);
18
+ $this->assertEquals ($yaml, $yaml_after_dump);
19
+ }
20
+ }
21
+
22
+ public function testDumpWithQuotes() {
23
+ $Spyc = new Spyc();
24
+ $Spyc->setting_dump_force_quotes = true;
25
+ foreach ($this->files_to_test as $file) {
26
+ $yaml = $Spyc->load(file_get_contents($file));
27
+ $dump = $Spyc->dump ($yaml);
28
+ $yaml_after_dump = Spyc::YAMLLoad ($dump);
29
+ $this->assertEquals ($yaml, $yaml_after_dump);
30
+ }
31
+ }
32
+
33
+ public function testDumpArrays() {
34
+ $dump = Spyc::YAMLDump(array ('item1', 'item2', 'item3'));
35
+ $awaiting = "---\n- item1\n- item2\n- item3\n";
36
+ $this->assertEquals ($awaiting, $dump);
37
+ }
38
+
39
+ public function testNull() {
40
+ $dump = Spyc::YAMLDump(array('a' => 1, 'b' => null, 'c' => 3));
41
+ $awaiting = "---\na: 1\nb: null\nc: 3\n";
42
+ $this->assertEquals ($awaiting, $dump);
43
+ }
44
+
45
+ public function testNext() {
46
+ $array = array("aaa", "bbb", "ccc");
47
+ #set arrays internal pointer to next element
48
+ next($array);
49
+ $dump = Spyc::YAMLDump($array);
50
+ $awaiting = "---\n- aaa\n- bbb\n- ccc\n";
51
+ $this->assertEquals ($awaiting, $dump);
52
+ }
53
+
54
+ public function testDumpingMixedArrays() {
55
+ $array = array();
56
+ $array[] = 'Sequence item';
57
+ $array['The Key'] = 'Mapped value';
58
+ $array[] = array('A sequence','of a sequence');
59
+ $array[] = array('first' => 'A sequence','second' => 'of mapped values');
60
+ $array['Mapped'] = array('A sequence','which is mapped');
61
+ $array['A Note'] = 'What if your text is too long?';
62
+ $array['Another Note'] = 'If that is the case, the dumper will probably fold your text by using a block. Kinda like this.';
63
+ $array['The trick?'] = 'The trick is that we overrode the default indent, 2, to 4 and the default wordwrap, 40, to 60.';
64
+ $array['Old Dog'] = "And if you want\n to preserve line breaks, \ngo ahead!";
65
+ $array['key:withcolon'] = "Should support this to";
66
+
67
+ $yaml = Spyc::YAMLDump($array,4,60);
68
+ }
69
+
70
+ public function testMixed() {
71
+ $dump = Spyc::YAMLDump(array(0 => 1, 'b' => 2, 1 => 3));
72
+ $awaiting = "---\n0: 1\nb: 2\n1: 3\n";
73
+ $this->assertEquals ($awaiting, $dump);
74
+ }
75
+
76
+ public function testDumpNumerics() {
77
+ $dump = Spyc::YAMLDump(array ('404', '405', '500'));
78
+ $awaiting = "---\n- 404\n- 405\n- 500\n";
79
+ $this->assertEquals ($awaiting, $dump);
80
+ }
81
+
82
+ public function testDumpAsterisks() {
83
+ $dump = Spyc::YAMLDump(array ('*'));
84
+ $awaiting = "---\n- '*'\n";
85
+ $this->assertEquals ($awaiting, $dump);
86
+ }
87
+
88
+ public function testDumpAmpersands() {
89
+ $dump = Spyc::YAMLDump(array ('some' => '&foo'));
90
+ $awaiting = "---\nsome: '&foo'\n";
91
+ $this->assertEquals ($awaiting, $dump);
92
+ }
93
+
94
+ public function testDumpExclamations() {
95
+ $dump = Spyc::YAMLDump(array ('some' => '!foo'));
96
+ $awaiting = "---\nsome: '!foo'\n";
97
+ $this->assertEquals ($awaiting, $dump);
98
+ }
99
+
100
+ public function testDumpExclamations2() {
101
+ $dump = Spyc::YAMLDump(array ('some' => 'foo!'));
102
+ $awaiting = "---\nsome: foo!\n";
103
+ $this->assertEquals ($awaiting, $dump);
104
+ }
105
+
106
+ public function testDumpApostrophes() {
107
+ $dump = Spyc::YAMLDump(array ('some' => "'Biz' pimpt bedrijventerreinen"));
108
+ $awaiting = "---\nsome: \"'Biz' pimpt bedrijventerreinen\"\n";
109
+ $this->assertEquals ($awaiting, $dump);
110
+ }
111
+
112
+ public function testDumpNumericHashes() {
113
+ $dump = Spyc::YAMLDump(array ("titel"=> array("0" => "", 1 => "Dr.", 5 => "Prof.", 6 => "Prof. Dr.")));
114
+ $awaiting = "---\ntitel:\n 0:\n 1: Dr.\n 5: Prof.\n 6: Prof. Dr.\n";
115
+ $this->assertEquals ($awaiting, $dump);
116
+ }
117
+
118
+ public function testEmpty() {
119
+ $dump = Spyc::YAMLDump(array("foo" => array()));
120
+ $awaiting = "---\nfoo: [ ]\n";
121
+ $this->assertEquals ($awaiting, $dump);
122
+ }
123
+
124
+ public function testHashesInKeys() {
125
+ $dump = Spyc::YAMLDump(array ('#color' => '#ffffff'));
126
+ $awaiting = "---\n\"#color\": '#ffffff'\n";
127
+ $this->assertEquals ($awaiting, $dump);
128
+ }
129
+
130
+ }
includes/admin/uaparser/lib/spyc-0.5/tests/IndentTest.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once ("../spyc.php");
4
+
5
+ class IndentTest extends PHPUnit_Framework_TestCase {
6
+
7
+ protected $Y;
8
+
9
+ protected function setUp() {
10
+ $this->Y = Spyc::YAMLLoad("indent_1.yaml");
11
+ }
12
+
13
+ public function testIndent_1() {
14
+ $this->assertEquals (array ('child_1' => 2, 'child_2' => 0, 'child_3' => 1), $this->Y['root']);
15
+ }
16
+
17
+ public function testIndent_2() {
18
+ $this->assertEquals (array ('child_1' => 1, 'child_2' => 2), $this->Y['root2']);
19
+ }
20
+
21
+ public function testIndent_3() {
22
+ $this->assertEquals (array (array ('resolutions' => array (1024 => 768, 1920 => 1200), 'producer' => 'Nec')), $this->Y['display']);
23
+ }
24
+
25
+ public function testIndent_4() {
26
+ $this->assertEquals (array (
27
+ array ('resolutions' => array (1024 => 768)),
28
+ array ('resolutions' => array (1920 => 1200)),
29
+ ), $this->Y['displays']);
30
+ }
31
+
32
+ public function testIndent_5() {
33
+ $this->assertEquals (array (array (
34
+ 'row' => 0,
35
+ 'col' => 0,
36
+ 'headsets_affected' => array (
37
+ array (
38
+ 'ports' => array (0),
39
+ 'side' => 'left',
40
+ )
41
+ ),
42
+ 'switch_function' => array (
43
+ 'ics_ptt' => true
44
+ )
45
+ )), $this->Y['nested_hashes_and_seqs']);
46
+ }
47
+
48
+ public function testIndent_6() {
49
+ $this->assertEquals (array (
50
+ 'h' => array (
51
+ array ('a' => 'b', 'a1' => 'b1'),
52
+ array ('c' => 'd')
53
+ )
54
+ ), $this->Y['easier_nest']);
55
+ }
56
+
57
+ public function testIndent_space() {
58
+ $this->assertEquals ("By four\n spaces", $this->Y['one_space']);
59
+ }
60
+
61
+ public function testListAndComment() {
62
+ $this->assertEquals (array ('one', 'two', 'three'), $this->Y['list_and_comment']);
63
+ }
64
+
65
+ }
includes/admin/uaparser/lib/spyc-0.5/tests/ParseTest.php ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once 'PHPUnit/Framework.php';
4
+ require_once ("../spyc.php");
5
+
6
+ class ParseTest extends PHPUnit_Framework_TestCase {
7
+
8
+ protected $yaml;
9
+
10
+ protected function setUp() {
11
+ $this->yaml = spyc_load_file('../spyc.yaml');
12
+ }
13
+
14
+ public function testMergeHashKeys() {
15
+ $Expected = array (
16
+ array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '1mm')),
17
+ array ('step' => array('instrument' => 'Lasik 2000', 'pulseEnergy' => 5.4, 'pulseDuration' => 12, 'repetition' => 1000, 'spotSize' => '2mm')),
18
+ );
19
+ $Actual = spyc_load_file ('indent_1.yaml');
20
+ $this->assertEquals ($Expected, $Actual['steps']);
21
+ }
22
+
23
+ public function testDeathMasks() {
24
+ $Expected = array ('sad' => 2, 'magnificent' => 4);
25
+ $Actual = spyc_load_file ('indent_1.yaml');
26
+ $this->assertEquals ($Expected, $Actual['death masks are']);
27
+ }
28
+
29
+ public function testDevDb() {
30
+ $Expected = array ('adapter' => 'mysql', 'host' => 'localhost', 'database' => 'rails_dev');
31
+ $Actual = spyc_load_file ('indent_1.yaml');
32
+ $this->assertEquals ($Expected, $Actual['development']);
33
+ }
34
+
35
+ public function testNumericKey() {
36
+ $this->assertEquals ("Ooo, a numeric key!", $this->yaml[1040]);
37
+ }
38
+
39
+ public function testMappingsString() {
40
+ $this->assertEquals ("Anyone's name, really.", $this->yaml['String']);
41
+ }
42
+
43
+ public function testMappingsInt() {
44
+ $this->assertSame (13, $this->yaml['Int']);
45
+ }
46
+
47
+ public function testMappingsBooleanTrue() {
48
+ $this->assertSame (true, $this->yaml['True']);
49
+ }
50
+
51
+ public function testMappingsBooleanFalse() {
52
+ $this->assertSame (false, $this->yaml['False']);
53
+ }
54
+
55
+ public function testMappingsZero() {
56
+ $this->assertSame (0, $this->yaml['Zero']);
57
+ }
58
+
59
+ public function testMappingsNull() {
60
+ $this->assertSame (null, $this->yaml['Null']);
61
+ }
62
+
63
+ public function testMappingsNotNull() {
64
+ $this->assertSame ('null', $this->yaml['NotNull']);
65
+ }
66
+
67
+ public function testMappingsFloat() {
68
+ $this->assertSame (5.34, $this->yaml['Float']);
69
+ }
70
+
71
+ public function testMappingsNegative() {
72
+ $this->assertSame (-90, $this->yaml['Negative']);
73
+ }
74
+
75
+ public function testMappingsSmallFloat() {
76
+ $this->assertSame (0.7, $this->yaml['SmallFloat']);
77
+ }
78
+
79
+ public function testNewline() {
80
+ $this->assertSame ("\n", $this->yaml['NewLine']);
81
+ }
82
+
83
+
84
+ public function testSeq0() {
85
+ $this->assertEquals ("PHP Class", $this->yaml[0]);
86
+ }
87
+
88
+ public function testSeq1() {
89
+ $this->assertEquals ("Basic YAML Loader", $this->yaml[1]);
90
+ }
91
+
92
+ public function testSeq2() {
93
+ $this->assertEquals ("Very Basic YAML Dumper", $this->yaml[2]);
94
+ }
95
+
96
+ public function testSeq3() {
97
+ $this->assertEquals (array("YAML is so easy to learn.",
98
+ "Your config files will never be the same."), $this->yaml[3]);
99
+ }
100
+
101
+ public function testSeqMap() {
102
+ $this->assertEquals (array("cpu" => "1.5ghz", "ram" => "1 gig",
103
+ "os" => "os x 10.4.1"), $this->yaml[4]);
104
+ }
105
+
106
+ public function testMappedSequence() {
107
+ $this->assertEquals (array("yaml.org", "php.net"), $this->yaml['domains']);
108
+ }
109
+
110
+ public function testAnotherSequence() {
111
+ $this->assertEquals (array("program" => "Adium", "platform" => "OS X",
112
+ "type" => "Chat Client"), $this->yaml[5]);
113
+ }
114
+
115
+ public function testFoldedBlock() {
116
+ $this->assertEquals ("There isn't any time for your tricks!\nDo you understand?", $this->yaml['no time']);
117
+ }
118
+
119
+ public function testLiteralAsMapped() {
120
+ $this->assertEquals ("There is nothing but time\nfor your tricks.", $this->yaml['some time']);
121
+ }
122
+
123
+ public function testCrazy() {
124
+ $this->assertEquals (array( array("name" => "spartan", "notes" =>
125
+ array( "Needs to be backed up",
126
+ "Needs to be normalized" ),
127
+ "type" => "mysql" )), $this->yaml['databases']);
128
+ }
129
+
130
+ public function testColons() {
131
+ $this->assertEquals ("like", $this->yaml["if: you'd"]);
132
+ }
133
+
134
+ public function testInline() {
135
+ $this->assertEquals (array("One", "Two", "Three", "Four"), $this->yaml[6]);
136
+ }
137
+
138
+ public function testNestedInline() {
139
+ $this->assertEquals (array("One", array("Two", "And", "Three"), "Four", "Five"), $this->yaml[7]);
140
+ }
141
+
142
+ public function testNestedNestedInline() {
143
+ $this->assertEquals (array( "This", array("Is", "Getting", array("Ridiculous", "Guys")),
144
+ "Seriously", array("Show", "Mercy")), $this->yaml[8]);
145
+ }
146
+
147
+ public function testInlineMappings() {
148
+ $this->assertEquals (array("name" => "chris", "age" => "young", "brand" => "lucky strike"), $this->yaml[9]);
149
+ }
150
+
151
+ public function testNestedInlineMappings() {
152
+ $this->assertEquals (array("name" => "mark", "age" => "older than chris",
153
+ "brand" => array("marlboro", "lucky strike")), $this->yaml[10]);
154
+ }
155
+
156
+ public function testReferences() {
157
+ $this->assertEquals (array('Perl', 'Python', 'PHP', 'Ruby'), $this->yaml['dynamic languages']);
158
+ }
159
+
160
+ public function testReferences2() {
161
+ $this->assertEquals (array('C/C++', 'Java'), $this->yaml['compiled languages']);
162
+ }
163
+
164
+ public function testReferences3() {
165
+ $this->assertEquals (array(
166
+ array('Perl', 'Python', 'PHP', 'Ruby'),
167
+ array('C/C++', 'Java')
168
+ ), $this->yaml['all languages']);
169
+ }
170
+
171
+ public function testEscapedQuotes() {
172
+ $this->assertEquals ("you know, this shouldn't work. but it does.", $this->yaml[11]);
173
+ }
174
+
175
+ public function testEscapedQuotes_2() {
176
+ $this->assertEquals ( "that's my value.", $this->yaml[12]);
177
+ }
178
+
179
+ public function testEscapedQuotes_3() {
180
+ $this->assertEquals ("again, that's my value.", $this->yaml[13]);
181
+ }
182
+
183
+ public function testQuotes() {
184
+ $this->assertEquals ("here's to \"quotes\", boss.", $this->yaml[14]);
185
+ }
186
+
187
+ public function testQuoteSequence() {
188
+ $this->assertEquals ( array( 'name' => "Foo, Bar's", 'age' => 20), $this->yaml[15]);
189
+ }
190
+
191
+ public function testShortSequence() {
192
+ $this->assertEquals (array( 0 => "a", 1 => array (0 => 1, 1 => 2), 2 => "b"), $this->yaml[16]);
193
+ }
194
+
195
+ public function testHash_1() {
196
+ $this->assertEquals ("Hash", $this->yaml['hash_1']);
197
+ }
198
+
199
+ public function testHash_2() {
200
+ $this->assertEquals ('Hash #and a comment', $this->yaml['hash_2']);
201
+ }
202
+
203
+ public function testHash_3() {
204
+ $this->assertEquals ('Hash (#) can appear in key too', $this->yaml['hash#3']);
205
+ }
206
+
207
+ public function testEndloop() {
208
+ $this->assertEquals ("Does this line in the end indeed make Spyc go to an infinite loop?", $this->yaml['endloop']);
209
+ }
210
+
211
+ public function testReallyLargeNumber() {
212
+ $this->assertEquals ('115792089237316195423570985008687907853269984665640564039457584007913129639936', $this->yaml['a_really_large_number']);
213
+ }
214
+
215
+ public function testFloatWithZeros() {
216
+ $this->assertSame ('1.0', $this->yaml['float_test']);
217
+ }
218
+
219
+ public function testFloatWithQuotes() {
220
+ $this->assertSame ('1.0', $this->yaml['float_test_with_quotes']);
221
+ }
222
+
223
+ public function testFloatInverse() {
224
+ $this->assertEquals ('001', $this->yaml['float_inverse_test']);
225
+ }
226
+
227
+ public function testIntArray() {
228
+ $this->assertEquals (array (1, 2, 3), $this->yaml['int array']);
229
+ }
230
+
231
+ public function testArrayOnSeveralLines() {
232
+ $this->assertEquals (array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), $this->yaml['array on several lines']);
233
+ }
234
+
235
+ public function testmoreLessKey() {
236
+ $this->assertEquals ('<value>', $this->yaml['morelesskey']);
237
+ }
238
+
239
+ public function testArrayOfZero() {
240
+ $this->assertSame (array(0), $this->yaml['array_of_zero']);
241
+ }
242
+
243
+ public function testSophisticatedArrayOfZero() {
244
+ $this->assertSame (array('rx' => array ('tx' => array (0))), $this->yaml['sophisticated_array_of_zero']);
245
+ }
246
+
247
+ public function testSwitches() {
248
+ $this->assertEquals (array (array ('row' => 0, 'col' => 0, 'func' => array ('tx' => array(0, 1)))), $this->yaml['switches']);
249
+ }
250
+
251
+ public function testEmptySequence() {
252
+ $this->assertSame (array(), $this->yaml['empty_sequence']);
253
+ }
254
+
255
+ public function testEmptyHash() {
256
+ $this->assertSame (array(), $this->yaml['empty_hash']);
257
+ }
258
+
259
+ public function testEmptykey() {
260
+ $this->assertSame (array('' => array ('key' => 'value')), $this->yaml['empty_key']);
261
+ }
262
+
263
+ public function testMultilines() {
264
+ $this->assertSame (array(array('type' => 'SomeItem', 'values' => array ('blah', 'blah', 'blah', 'blah'), 'ints' => array(2, 54, 12, 2143))), $this->yaml['multiline_items']);
265
+ }
266
+
267
+ public function testManyNewlines() {
268
+ $this->assertSame ('A quick
269
+ fox
270
+
271
+
272
+ jumped
273
+ over
274
+
275
+
276
+
277
+
278
+
279
+ a lazy
280
+
281
+
282
+
283
+ dog', $this->yaml['many_lines']);
284
+ }
285
+
286
+ public function testWerte() {
287
+ $this->assertSame (array ('1' => 'nummer 1', '0' => 'Stunde 0'), $this->yaml['werte']);
288
+ }
289
+
290
+ /* public function testNoIndent() {
291
+ $this->assertSame (array(
292
+ array ('record1'=>'value1'),
293
+ array ('record2'=>'value2')
294
+ )
295
+ , $this->yaml['noindent_records']);
296
+ } */
297
+
298
+ public function testColonsInKeys() {
299
+ $this->assertSame (array (1000), $this->yaml['a:1']);
300
+ }
301
+
302
+ public function testColonsInKeys2() {
303
+ $this->assertSame (array (2000), $this->yaml['a:2']);
304
+ }
305
+
306
+ public function testSpecialCharacters() {
307
+ $this->assertSame ('[{]]{{]]', $this->yaml['special_characters']);
308
+ }
309
+
310
+ public function testAngleQuotes() {
311
+ $Quotes = Spyc::YAMLLoad('quotes.yaml');
312
+ $this->assertEquals (array ('html_tags' => array ('<br>', '<p>'), 'html_content' => array ('<p>hello world</p>', 'hello<br>world'), 'text_content' => array ('hello world')),
313
+ $Quotes);
314
+ }
315
+
316
+ public function testFailingColons() {
317
+ $Failing = Spyc::YAMLLoad('failing1.yaml');
318
+ $this->assertSame (array ('MyObject' => array ('Prop1' => array ('key1:val1'))),
319
+ $Failing);
320
+ }
321
+
322
+ }
includes/admin/uaparser/lib/spyc-0.5/tests/RoundTripTest.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once ("../spyc.php");
4
+
5
+ function roundTrip($a) { return Spyc::YAMLLoad(Spyc::YAMLDump(array('x' => $a))); }
6
+
7
+
8
+ class RoundTripTest extends PHPUnit_Framework_TestCase {
9
+
10
+ protected function setUp() {
11
+ }
12
+
13
+ public function testNull() {
14
+ $this->assertEquals (array ('x' => null), roundTrip (null));
15
+ }
16
+
17
+ public function testY() {
18
+ $this->assertEquals (array ('x' => 'y'), roundTrip ('y'));
19
+ }
20
+
21
+ public function testExclam() {
22
+ $this->assertEquals (array ('x' => '!yeah'), roundTrip ('!yeah'));
23
+ }
24
+
25
+ public function test5() {
26
+ $this->assertEquals (array ('x' => '5'), roundTrip ('5'));
27
+ }
28
+
29
+ public function testSpaces() {
30
+ $this->assertEquals (array ('x' => 'x '), roundTrip ('x '));
31
+ }
32
+
33
+ public function testApostrophes() {
34
+ $this->assertEquals (array ('x' => "'biz'"), roundTrip ("'biz'"));
35
+ }
36
+
37
+ public function testNewLines() {
38
+ $this->assertEquals (array ('x' => "\n"), roundTrip ("\n"));
39
+ }
40
+
41
+ public function testHashes() {
42
+ $this->assertEquals (array ('x' => array ("#color" => '#fff')), roundTrip (array ("#color" => '#fff')));
43
+ }
44
+
45
+ public function testWordWrap() {
46
+ $this->assertEquals (array ('x' => "aaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), roundTrip ("aaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"));
47
+ }
48
+
49
+ public function testABCD() {
50
+ $this->assertEquals (array ('a', 'b', 'c', 'd'), Spyc::YAMLLoad(Spyc::YAMLDump(array('a', 'b', 'c', 'd'))));
51
+ }
52
+
53
+ public function testABCD2() {
54
+ $a = array('a', 'b', 'c', 'd'); // Create a simple list
55
+ $b = Spyc::YAMLDump($a); // Dump the list as YAML
56
+ $c = Spyc::YAMLLoad($b); // Load the dumped YAML
57
+ $d = Spyc::YAMLDump($c); // Re-dump the data
58
+ $this->assertSame($b, $d);
59
+ }
60
+
61
+ }
includes/admin/uaparser/lib/spyc-0.5/tests/failing1.yaml ADDED
@@ -0,0 +1,2 @@
 
 
1
+ MyObject:
2
+ Prop1: {key1:val1}
includes/admin/uaparser/lib/spyc-0.5/tests/indent_1.yaml ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ root:
2
+ child_1: 2
3
+
4
+ child_2: 0
5
+ child_3: 1
6
+
7
+ root2:
8
+ child_1: 1
9
+ # A comment
10
+ child_2: 2
11
+
12
+ displays:
13
+ - resolutions:
14
+ 1024: 768
15
+ - resolutions:
16
+ 1920: 1200
17
+
18
+ display:
19
+ - resolutions:
20
+ 1024: 768
21
+ 1920: 1200
22
+ producer: "Nec"
23
+
24
+ nested_hashes_and_seqs:
25
+ - { row: 0, col: 0, headsets_affected: [{ports: [0], side: left}], switch_function: {ics_ptt: true} }
26
+
27
+ easier_nest: { h: [{a: b, a1: b1}, {c: d}] }
28
+
29
+ one_space: |
30
+ By four
31
+ spaces
32
+
33
+ steps:
34
+ - step: &id001
35
+ instrument: Lasik 2000
36
+ pulseEnergy: 5.4
37
+ pulseDuration: 12
38
+ repetition: 1000
39
+ spotSize: 1mm
40
+ - step:
41
+ <<: *id001
42
+ spotSize: 2mm
43
+
44
+ death masks are:
45
+ sad: 2
46
+ <<: {magnificent: 4}
47
+
48
+ login: &login
49
+ adapter: mysql
50
+ host: localhost
51
+
52
+ development:
53
+ database: rails_dev
54
+ <<: *login
55
+
56
+ "key": "value:"
57
+ colon_only: ":"
58
+
59
+ list_and_comment: [one, two, three] # comment
includes/admin/uaparser/lib/spyc-0.5/tests/quotes.yaml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ html_tags:
2
+ - <br>
3
+ - <p>
4
+ html_content:
5
+ - <p>hello world</p>
6
+ - hello<br>world
7
+ text_content:
8
+ - hello world
includes/admin/uaparser/log/.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
1
+ *.txt
2
+ *.log
includes/admin/uaparser/log/README.md ADDED
@@ -0,0 +1 @@
 
1
+ If you turn on debug mode this is where your results will be logged.
includes/admin/uaparser/resources/regexes.json ADDED
@@ -0,0 +1,1193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "user_agent_parsers": [
3
+ {
4
+ "regex": "(SeaMonkey|Camino)/(\\d+)\\.(\\d+)\\.?([ab]?\\d+[a-z]*)"
5
+ },
6
+ {
7
+ "regex": "(Pale[Mm]oon)/(\\d+)\\.(\\d+)\\.?(\\d+)?",
8
+ "family_replacement": "Pale Moon (Firefox Variant)"
9
+ },
10
+ {
11
+ "regex": "(Fennec)/(\\d+)\\.(\\d+)\\.?([ab]?\\d+[a-z]*)",
12
+ "family_replacement": "Firefox Mobile"
13
+ },
14
+ {
15
+ "regex": "(Fennec)/(\\d+)\\.(\\d+)(pre)",
16
+ "family_replacement": "Firefox Mobile"
17
+ },
18
+ {
19
+ "regex": "(Fennec)/(\\d+)\\.(\\d+)",
20
+ "family_replacement": "Firefox Mobile"
21
+ },
22
+ {
23
+ "regex": "Mobile.*(Firefox)/(\\d+)\\.(\\d+)",
24
+ "family_replacement": "Firefox Mobile"
25
+ },
26
+ {
27
+ "regex": "(Namoroka|Shiretoko|Minefield)/(\\d+)\\.(\\d+)\\.(\\d+(?:pre)?)",
28
+ "family_replacement": "Firefox ($1)"
29
+ },
30
+ {
31
+ "regex": "(Firefox)/(\\d+)\\.(\\d+)(a\\d+[a-z]*)",
32
+ "family_replacement": "Firefox Alpha"
33
+ },
34
+ {
35
+ "regex": "(Firefox)/(\\d+)\\.(\\d+)(b\\d+[a-z]*)",
36
+ "family_replacement": "Firefox Beta"
37
+ },
38
+ {
39
+ "regex": "(Firefox)-(?:\\d+\\.\\d+)?/(\\d+)\\.(\\d+)(a\\d+[a-z]*)",
40
+ "family_replacement": "Firefox Alpha"
41
+ },
42
+ {
43
+ "regex": "(Firefox)-(?:\\d+\\.\\d+)?/(\\d+)\\.(\\d+)(b\\d+[a-z]*)",
44
+ "family_replacement": "Firefox Beta"
45
+ },
46
+ {
47
+ "regex": "(Namoroka|Shiretoko|Minefield)/(\\d+)\\.(\\d+)([ab]\\d+[a-z]*)?",
48
+ "family_replacement": "Firefox ($1)"
49
+ },
50
+ {
51
+ "regex": "(Firefox).*Tablet browser (\\d+)\\.(\\d+)\\.(\\d+)",
52
+ "family_replacement": "MicroB"
53
+ },
54
+ {
55
+ "regex": "(MozillaDeveloperPreview)/(\\d+)\\.(\\d+)([ab]\\d+[a-z]*)?"
56
+ },
57
+ {
58
+ "regex": "(Flock)/(\\d+)\\.(\\d+)(b\\d+?)"
59
+ },
60
+ {
61
+ "regex": "(RockMelt)/(\\d+)\\.(\\d+)\\.(\\d+)"
62
+ },
63
+ {
64
+ "regex": "(Navigator)/(\\d+)\\.(\\d+)\\.(\\d+)",
65
+ "family_replacement": "Netscape"
66
+ },
67
+ {
68
+ "regex": "(Navigator)/(\\d+)\\.(\\d+)([ab]\\d+)",
69
+ "family_replacement": "Netscape"
70
+ },
71
+ {
72
+ "regex": "(Netscape6)/(\\d+)\\.(\\d+)\\.(\\d+)",
73
+ "family_replacement": "Netscape"
74
+ },
75
+ {
76
+ "regex": "(MyIBrow)/(\\d+)\\.(\\d+)",
77
+ "family_replacement": "My Internet Browser"
78
+ },
79
+ {
80
+ "regex": "(Opera Tablet).*Version/(\\d+)\\.(\\d+)(?:\\.(\\d+))?"
81
+ },
82
+ {
83
+ "regex": "(Opera)/.+Opera Mobi.+Version/(\\d+)\\.(\\d+)",
84
+ "family_replacement": "Opera Mobile"
85
+ },
86
+ {
87
+ "regex": "Opera Mobi",
88
+ "family_replacement": "Opera Mobile"
89
+ },
90
+ {
91
+ "regex": "(Opera Mini)/(\\d+)\\.(\\d+)"
92
+ },
93
+ {
94
+ "regex": "(Opera Mini)/att/(\\d+)\\.(\\d+)"
95
+ },
96
+ {
97
+ "regex": "(Opera)/9.80.*Version/(\\d+)\\.(\\d+)(?:\\.(\\d+))?"
98
+ },
99
+ {
100
+ "regex": "(?:Mobile Safari).*(OPR)/(\\d+)\\.(\\d+)\\.(\\d+)",
101
+ "family_replacement": "Opera Mobile"
102
+ },
103
+ {
104
+ "regex": "(?:Chrome).*(OPR)/(\\d+)\\.(\\d+)\\.(\\d+)",
105
+ "family_replacement": "Opera"
106
+ },
107
+ {
108
+ "regex": "(hpw|web)OS/(\\d+)\\.(\\d+)(?:\\.(\\d+))?",
109
+ "family_replacement": "webOS Browser"
110
+ },
111
+ {
112
+ "regex": "(luakit)",
113
+ "family_replacement": "LuaKit"
114
+ },
115
+ {
116
+ "regex": "(Snowshoe)/(\\d+)\\.(\\d+).(\\d+)"
117
+ },
118
+ {
119
+ "regex": "(Lightning)/(\\d+)\\.(\\d+)([ab]?\\d+[a-z]*)"
120
+ },
121
+ {
122
+ "regex": "(Firefox)/(\\d+)\\.(\\d+)\\.(\\d+(?:pre)?) \\(Swiftfox\\)",
123
+ "family_replacement": "Swiftfox"
124
+ },
125
+ {
126
+ "regex": "(Firefox)/(\\d+)\\.(\\d+)([ab]\\d+[a-z]*)? \\(Swiftfox\\)",
127
+ "family_replacement": "Swiftfox"
128
+ },
129
+ {
130
+ "regex": "(rekonq)/(\\d+)\\.(\\d+)\\.?(\\d+)? Safari",
131
+ "family_replacement": "Rekonq"
132
+ },
133
+ {
134
+ "regex": "rekonq",
135
+ "family_replacement": "Rekonq"
136
+ },
137
+ {
138
+ "regex": "(conkeror|Conkeror)/(\\d+)\\.(\\d+)\\.?(\\d+)?",
139
+ "family_replacement": "Conkeror"
140
+ },
141
+ {
142
+ "regex": "(konqueror)/(\\d+)\\.(\\d+)\\.(\\d+)",
143
+ "family_replacement": "Konqueror"
144
+ },
145
+ {
146
+ "regex": "(WeTab)-Browser"
147
+ },
148
+ {
149
+ "regex": "(Comodo_Dragon)/(\\d+)\\.(\\d+)\\.(\\d+)",
150
+ "family_replacement": "Comodo Dragon"
151
+ },
152
+ {
153
+ "regex": "(YottaaMonitor|BrowserMob|HttpMonitor|YandexBot|Slurp|BingPreview|PagePeeker|ThumbShotsBot|WebThumb|URL2PNG|ZooShot|GomezA|Catchpoint bot|Willow Internet Crawler|Google SketchUp|Read%20Later)"
154
+ },
155
+ {
156
+ "regex": "(Symphony) (\\d+).(\\d+)"
157
+ },
158
+ {
159
+ "regex": "(Minimo)"
160
+ },
161
+ {
162
+ "regex": "(CrMo)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)",
163
+ "family_replacement": "Chrome Mobile"
164
+ },
165
+ {
166
+ "regex": "(CriOS)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)",
167
+ "family_replacement": "Chrome Mobile iOS"
168
+ },
169
+ {
170
+ "regex": "(Chrome)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+) Mobile",
171
+ "family_replacement": "Chrome Mobile"
172
+ },
173
+ {
174
+ "regex": "(chromeframe)/(\\d+)\\.(\\d+)\\.(\\d+)",
175
+ "family_replacement": "Chrome Frame"
176
+ },
177
+ {
178
+ "regex": "(UCBrowser)[ /](\\d+)\\.(\\d+)\\.(\\d+)",
179
+ "family_replacement": "UC Browser"
180
+ },
181
+ {
182
+ "regex": "(UC Browser)[ /](\\d+)\\.(\\d+)\\.(\\d+)"
183
+ },
184
+ {
185
+ "regex": "(UC Browser|UCBrowser|UCWEB)(\\d+)\\.(\\d+)\\.(\\d+)",
186
+ "family_replacement": "UC Browser"
187
+ },
188
+ {
189
+ "regex": "(SLP Browser)/(\\d+)\\.(\\d+)",
190
+ "family_replacement": "Tizen Browser"
191
+ },
192
+ {
193
+ "regex": "(SE 2\\.X) MetaSr (\\d+)\\.(\\d+)",
194
+ "family_replacement": "Sogou Explorer"
195
+ },
196
+ {
197
+ "regex": "(baidubrowser)[/\\s](\\d+)",
198
+ "family_replacement": "Baidu Browser"
199
+ },
200
+ {
201
+ "regex": "(FlyFlow)/(\\d+)\\.(\\d+)",
202
+ "family_replacement": "Baidu Explorer"
203
+ },
204
+ {
205
+ "regex": "(Pingdom.com_bot_version_)(\\d+)\\.(\\d+)",
206
+ "family_replacement": "PingdomBot"
207
+ },
208
+ {
209
+ "regex": "(facebookexternalhit)/(\\d+)\\.(\\d+)",
210
+ "family_replacement": "FacebookBot"
211
+ },
212
+ {
213
+ "regex": "(Twitterbot)/(\\d+)\\.(\\d+)",
214
+ "family_replacement": "TwitterBot"
215
+ },
216
+ {
217
+ "regex": "(Rackspace Monitoring)/(\\d+)\\.(\\d+)",
218
+ "family_replacement": "RackspaceBot"
219
+ },
220
+ {
221
+ "regex": "(PyAMF)/(\\d+)\\.(\\d+)\\.(\\d+)"
222
+ },
223
+ {
224
+ "regex": "(YaBrowser)/(\\d+)\\.(\\d+)\\.(\\d+)",
225
+ "family_replacement": "Yandex Browser"
226
+ },
227
+ {
228
+ "regex": "(Chrome)/(\\d+)\\.(\\d+)\\.(\\d+).* MRCHROME",
229
+ "family_replacement": "Mail.ru Chromium Browser"
230
+ },
231
+ {
232
+ "regex": "(AOL) (\\d+)\\.(\\d+); AOLBuild (\\d+)"
233
+ },
234
+ {
235
+ "regex": "(AdobeAIR|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Epiphany|Shiira|Sunrise|Flock|Netscape|Lunascape|WebPilot|Vodafone|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave)/(\\d+)\\.(\\d+)\\.(\\d+)"
236
+ },
237
+ {
238
+ "regex": "(Chromium|Chrome)/(\\d+)\\.(\\d+)\\.(\\d+)"
239
+ },
240
+ {
241
+ "regex": "(Bolt|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|Vodafone|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser)/(\\d+)\\.(\\d+)"
242
+ },
243
+ {
244
+ "regex": "(Chromium|Chrome)/(\\d+)\\.(\\d+)"
245
+ },
246
+ {
247
+ "regex": "(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) (\\d+)\\.(\\d+)\\.(\\d+)"
248
+ },
249
+ {
250
+ "regex": "(iCab|Lunascape|Opera|Android|Jasmine|Polaris) (\\d+)\\.(\\d+)\\.?(\\d+)?"
251
+ },
252
+ {
253
+ "regex": "(Kindle)/(\\d+)\\.(\\d+)"
254
+ },
255
+ {
256
+ "regex": "(Android) Donut",
257
+ "v2_replacement": "2",
258
+ "v1_replacement": "1"
259
+ },
260
+ {
261
+ "regex": "(Android) Eclair",
262
+ "v2_replacement": "1",
263
+ "v1_replacement": "2"
264
+ },
265
+ {
266
+ "regex": "(Android) Froyo",
267
+ "v2_replacement": "2",
268
+ "v1_replacement": "2"
269
+ },
270
+ {
271
+ "regex": "(Android) Gingerbread",
272
+ "v2_replacement": "3",
273
+ "v1_replacement": "2"
274
+ },
275
+ {
276
+ "regex": "(Android) Honeycomb",
277
+ "v1_replacement": "3"
278
+ },
279
+ {
280
+ "regex": "(IEMobile)[ /](\\d+)\\.(\\d+)",
281
+ "family_replacement": "IE Mobile"
282
+ },
283
+ {
284
+ "regex": "(MSIE) (\\d+)\\.(\\d+).*XBLWP7",
285
+ "family_replacement": "IE Large Screen"
286
+ },
287
+ {
288
+ "regex": "(Firefox)/(\\d+)\\.(\\d+)\\.(\\d+)"
289
+ },
290
+ {
291
+ "regex": "(Firefox)/(\\d+)\\.(\\d+)(pre|[ab]\\d+[a-z]*)?"
292
+ },
293
+ {
294
+ "regex": "(Obigo)InternetBrowser"
295
+ },
296
+ {
297
+ "regex": "(Obigo)\\-Browser"
298
+ },
299
+ {
300
+ "regex": "(Obigo|OBIGO)[^\\d]*(\\d+)(?:.(\\d+))?",
301
+ "family_replacement": "Obigo"
302
+ },
303
+ {
304
+ "regex": "(MAXTHON|Maxthon) (\\d+)\\.(\\d+)",
305
+ "family_replacement": "Maxthon"
306
+ },
307
+ {
308
+ "regex": "(Maxthon|MyIE2|Uzbl|Shiira)",
309
+ "v1_replacement": "0"
310
+ },
311
+ {
312
+ "regex": "PLAYSTATION 3.+WebKit",
313
+ "family_replacement": "NetFront NX"
314
+ },
315
+ {
316
+ "regex": "PLAYSTATION 3",
317
+ "family_replacement": "NetFront"
318
+ },
319
+ {
320
+ "regex": "(PlayStation Portable)",
321
+ "family_replacement": "NetFront"
322
+ },
323
+ {
324
+ "regex": "(PlayStation Vita)",
325
+ "family_replacement": "NetFront NX"
326
+ },
327
+ {
328
+ "regex": "AppleWebKit.+ (NX)/(\\d+)\\.(\\d+)\\.(\\d+)",
329
+ "family_replacement": "NetFront NX"
330
+ },
331
+ {
332
+ "regex": "(Nintendo 3DS)",
333
+ "family_replacement": "NetFront NX"
334
+ },
335
+ {
336
+ "regex": "(BrowseX) \\((\\d+)\\.(\\d+)\\.(\\d+)"
337
+ },
338
+ {
339
+ "regex": "(NCSA_Mosaic)/(\\d+)\\.(\\d+)",
340
+ "family_replacement": "NCSA Mosaic"
341
+ },
342
+ {
343
+ "regex": "(POLARIS)/(\\d+)\\.(\\d+)",
344
+ "family_replacement": "Polaris"
345
+ },
346
+ {
347
+ "regex": "(Embider)/(\\d+)\\.(\\d+)",
348
+ "family_replacement": "Polaris"
349
+ },
350
+ {
351
+ "regex": "(BonEcho)/(\\d+)\\.(\\d+)\\.(\\d+)",
352
+ "family_replacement": "Bon Echo"
353
+ },
354
+ {
355
+ "regex": "M?QQBrowser",
356
+ "family_replacement": "QQ Browser"
357
+ },
358
+ {
359
+ "regex": "(iPod).+Version/(\\d+)\\.(\\d+)\\.(\\d+)",
360
+ "family_replacement": "Mobile Safari"
361
+ },
362
+ {
363
+ "regex": "(iPod).*Version/(\\d+)\\.(\\d+)",
364
+ "family_replacement": "Mobile Safari"
365
+ },
366
+ {
367
+ "regex": "(iPhone).*Version/(\\d+)\\.(\\d+)\\.(\\d+)",
368
+ "family_replacement": "Mobile Safari"
369
+ },
370
+ {
371
+ "regex": "(iPhone).*Version/(\\d+)\\.(\\d+)",
372
+ "family_replacement": "Mobile Safari"
373
+ },
374
+ {
375
+ "regex": "(iPad).*Version/(\\d+)\\.(\\d+)\\.(\\d+)",
376
+ "family_replacement": "Mobile Safari"
377
+ },
378
+ {
379
+ "regex": "(iPad).*Version/(\\d+)\\.(\\d+)",
380
+ "family_replacement": "Mobile Safari"
381
+ },
382
+ {
383
+ "regex": "(iPod|iPhone|iPad);.*CPU.*OS (\\d+)(?:_\\d+)?_(\\d+).*Mobile",
384
+ "family_replacement": "Mobile Safari"
385
+ },
386
+ {
387
+ "regex": "(iPod|iPhone|iPad)",
388
+ "family_replacement": "Mobile Safari"
389
+ },
390
+ {
391
+ "regex": "(AvantGo) (\\d+).(\\d+)"
392
+ },
393
+ {
394
+ "regex": "(OneBrowser)/(\\d+).(\\d+)",
395
+ "family_replacement": "ONE Browser"
396
+ },
397
+ {
398
+ "regex": "(Avant)",
399
+ "v1_replacement": "1"
400
+ },
401
+ {
402
+ "regex": "(QtCarBrowser)",
403
+ "v1_replacement": "1"
404
+ },
405
+ {
406
+ "regex": "(iBrowser/Mini)(\\d+).(\\d+)",
407
+ "family_replacement": "iBrowser Mini"
408
+ },
409
+ {
410
+ "regex": "^(Nokia)",
411
+ "family_replacement": "Nokia Services (WAP) Browser"
412
+ },
413
+ {
414
+ "regex": "(NokiaBrowser)/(\\d+)\\.(\\d+).(\\d+)\\.(\\d+)",
415
+ "family_replacement": "Nokia Browser"
416
+ },
417
+ {
418
+ "regex": "(NokiaBrowser)/(\\d+)\\.(\\d+).(\\d+)",
419
+ "family_replacement": "Nokia Browser"
420
+ },
421
+ {
422
+ "regex": "(NokiaBrowser)/(\\d+)\\.(\\d+)",
423
+ "family_replacement": "Nokia Browser"
424
+ },
425
+ {
426
+ "regex": "(BrowserNG)/(\\d+)\\.(\\d+).(\\d+)",
427
+ "family_replacement": "Nokia Browser"
428
+ },
429
+ {
430
+ "regex": "(Series60)/5\\.0",
431
+ "v2_replacement": "0",
432
+ "v1_replacement": "7",
433
+ "family_replacement": "Nokia Browser"
434
+ },
435
+ {
436
+ "regex": "(Series60)/(\\d+)\\.(\\d+)",
437
+ "family_replacement": "Nokia OSS Browser"
438
+ },
439
+ {
440
+ "regex": "(S40OviBrowser)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)",
441
+ "family_replacement": "Ovi Browser"
442
+ },
443
+ {
444
+ "regex": "(Nokia)[EN]?(\\d+)"
445
+ },
446
+ {
447
+ "regex": "(BB10);",
448
+ "family_replacement": "BlackBerry WebKit"
449
+ },
450
+ {
451
+ "regex": "(PlayBook).+RIM Tablet OS (\\d+)\\.(\\d+)\\.(\\d+)",
452
+ "family_replacement": "BlackBerry WebKit"
453
+ },
454
+ {
455
+ "regex": "(Black[bB]erry).+Version/(\\d+)\\.(\\d+)\\.(\\d+)",
456
+ "family_replacement": "BlackBerry WebKit"
457
+ },
458
+ {
459
+ "regex": "(Black[bB]erry)\\s?(\\d+)",
460
+ "family_replacement": "BlackBerry"
461
+ },
462
+ {
463
+ "regex": "(OmniWeb)/v(\\d+)\\.(\\d+)"
464
+ },
465
+ {
466
+ "regex": "(Blazer)/(\\d+)\\.(\\d+)",
467
+ "family_replacement": "Palm Blazer"
468
+ },
469
+ {
470
+ "regex": "(Pre)/(\\d+)\\.(\\d+)",
471
+ "family_replacement": "Palm Pre"
472
+ },
473
+ {
474
+ "regex": "(Links) \\((\\d+)\\.(\\d+)"
475
+ },
476
+ {
477
+ "regex": "(QtWeb) Internet Browser/(\\d+)\\.(\\d+)"
478
+ },
479
+ {
480
+ "regex": "(Silk)/(\\d+)\\.(\\d+)(?:\\.([0-9\\-]+))?",
481
+ "family_replacement": "Amazon Silk"
482
+ },
483
+ {
484
+ "regex": "(PhantomJS)/(\\d+)\\.(\\d+)\\.(\\d+)"
485
+ },
486
+ {
487
+ "regex": "(AppleWebKit)/(\\d+)\\.?(\\d+)?\\+ .* Safari",
488
+ "family_replacement": "WebKit Nightly"
489
+ },
490
+ {
491
+ "regex": "(Version)/(\\d+)\\.(\\d+)(?:\\.(\\d+))?.*Safari/",
492
+ "family_replacement": "Safari"
493
+ },
494
+ {
495
+ "regex": "(Safari)/\\d+"
496
+ },
497
+ {
498
+ "regex": "(OLPC)/Update(\\d+)\\.(\\d+)"
499
+ },
500
+ {
501
+ "regex": "(OLPC)/Update()\\.(\\d+)",
502
+ "v1_replacement": "0"
503
+ },
504
+ {
505
+ "regex": "(SEMC\\-Browser)/(\\d+)\\.(\\d+)"
506
+ },
507
+ {
508
+ "regex": "(Teleca)",
509
+ "family_replacement": "Teleca Browser"
510
+ },
511
+ {
512
+ "regex": "(Phantom)/V(\\d+)\\.(\\d+)",
513
+ "family_replacement": "Phantom Browser"
514
+ },
515
+ {
516
+ "regex": "([MS]?IE) (\\d+)\\.(\\d+)",
517
+ "family_replacement": "IE"
518
+ },
519
+ {
520
+ "regex": "(python-requests)/(\\d+)\\.(\\d+)",
521
+ "family_replacement": "Python Requests"
522
+ }
523
+ ],
524
+ "os_parsers": [
525
+ {
526
+ "regex": "(Android) (\\d+)\\.(\\d+)(?:[.\\-]([a-z0-9]+))?"
527
+ },
528
+ {
529
+ "regex": "(Android)\\-(\\d+)\\.(\\d+)(?:[.\\-]([a-z0-9]+))?"
530
+ },
531
+ {
532
+ "regex": "(Android) Donut",
533
+ "os_v2_replacement": "2",
534
+ "os_v1_replacement": "1"
535
+ },
536
+ {
537
+ "regex": "(Android) Eclair",
538
+ "os_v2_replacement": "1",
539
+ "os_v1_replacement": "2"
540
+ },
541
+ {
542
+ "regex": "(Android) Froyo",
543
+ "os_v2_replacement": "2",
544
+ "os_v1_replacement": "2"
545
+ },
546
+ {
547
+ "regex": "(Android) Gingerbread",
548
+ "os_v2_replacement": "3",
549
+ "os_v1_replacement": "2"
550
+ },
551
+ {
552
+ "regex": "(Android) Honeycomb",
553
+ "os_v1_replacement": "3"
554
+ },
555
+ {
556
+ "regex": "(Silk-Accelerated=[a-z]{4,5})",
557
+ "os_replacement": "Android"
558
+ },
559
+ {
560
+ "regex": "(Windows (?:NT 5\\.2|NT 5\\.1))",
561
+ "os_replacement": "Windows XP"
562
+ },
563
+ {
564
+ "regex": "(XBLWP7)",
565
+ "os_replacement": "Windows Phone"
566
+ },
567
+ {
568
+ "regex": "(Windows NT 6\\.1)",
569
+ "os_replacement": "Windows 7"
570
+ },
571
+ {
572
+ "regex": "(Windows NT 6\\.0)",
573
+ "os_replacement": "Windows Vista"
574
+ },
575
+ {
576
+ "regex": "(Win 9x 4\\.90)",
577
+ "os_replacement": "Windows Me"
578
+ },
579
+ {
580
+ "regex": "(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\\.0|Windows Vista|Windows 2000|Windows 3.1)"
581
+ },
582
+ {
583
+ "regex": "(Windows NT 6\\.2; ARM;)",
584
+ "os_replacement": "Windows RT"
585
+ },
586
+ {
587
+ "regex": "(Windows NT 6\\.2)",
588
+ "os_replacement": "Windows 8"
589
+ },
590
+ {
591
+ "regex": "(Windows NT 5\\.0)",
592
+ "os_replacement": "Windows 2000"
593
+ },
594
+ {
595
+ "regex": "(Windows Phone) (\\d+)\\.(\\d+)"
596
+ },
597
+ {
598
+ "regex": "(Windows Phone) OS (\\d+)\\.(\\d+)"
599
+ },
600
+ {
601
+ "regex": "(Windows ?Mobile)",
602
+ "os_replacement": "Windows Mobile"
603
+ },
604
+ {
605
+ "regex": "(WinNT4.0)",
606
+ "os_replacement": "Windows NT 4.0"
607
+ },
608
+ {
609
+ "regex": "(Win98)",
610
+ "os_replacement": "Windows 98"
611
+ },
612
+ {
613
+ "regex": "(Tizen)/(\\d+)\\.(\\d+)"
614
+ },
615
+ {
616
+ "regex": "(Mac OS X) (\\d+)[_.](\\d+)(?:[_.](\\d+))?"
617
+ },
618
+ {
619
+ "regex": "Mac_PowerPC",
620
+ "os_replacement": "Mac OS"
621
+ },
622
+ {
623
+ "regex": "(?:PPC|Intel) (Mac OS X)"
624
+ },
625
+ {
626
+ "regex": "(CPU OS|iPhone OS) (\\d+)_(\\d+)(?:_(\\d+))?",
627
+ "os_replacement": "iOS"
628
+ },
629
+ {
630
+ "regex": "(iPhone|iPad|iPod); Opera",
631
+ "os_replacement": "iOS"
632
+ },
633
+ {
634
+ "regex": "(iPhone|iPad|iPod).*Mac OS X.*Version/(\\d+)\\.(\\d+)",
635
+ "os_replacement": "iOS"
636
+ },
637
+ {
638
+ "regex": "(AppleTV)/(\\d+)\\.(\\d+)",
639
+ "os_replacement": "ATV OS X"
640
+ },
641
+ {
642
+ "regex": "(CrOS) [a-z0-9_]+ (\\d+)\\.(\\d+)(?:\\.(\\d+))?",
643
+ "os_replacement": "Chrome OS"
644
+ },
645
+ {
646
+ "regex": "([Dd]ebian)",
647
+ "os_replacement": "Debian"
648
+ },
649
+ {
650
+ "regex": "(Linux Mint)(?:/(\\d+))?"
651
+ },
652
+ {
653
+ "regex": "(Mandriva)(?: Linux)?/(?:[\\d.-]+m[a-z]{2}(\\d+).(\\d))?"
654
+ },
655
+ {
656
+ "regex": "(Symbian[Oo][Ss])/(\\d+)\\.(\\d+)",
657
+ "os_replacement": "Symbian OS"
658
+ },
659
+ {
660
+ "regex": "(Symbian/3).+NokiaBrowser/7\\.3",
661
+ "os_replacement": "Symbian^3 Anna"
662
+ },
663
+ {
664
+ "regex": "(Symbian/3).+NokiaBrowser/7\\.4",
665
+ "os_replacement": "Symbian^3 Belle"
666
+ },
667
+ {
668
+ "regex": "(Symbian/3)",
669
+ "os_replacement": "Symbian^3"
670
+ },
671
+ {
672
+ "regex": "(Series 60|SymbOS|S60)",
673
+ "os_replacement": "Symbian OS"
674
+ },
675
+ {
676
+ "regex": "(MeeGo)"
677
+ },
678
+ {
679
+ "regex": "Symbian [Oo][Ss]",
680
+ "os_replacement": "Symbian OS"
681
+ },
682
+ {
683
+ "regex": "Series40;",
684
+ "os_replacement": "Nokia Series 40"
685
+ },
686
+ {
687
+ "regex": "(BB10);.+Version/(\\d+)\\.(\\d+)\\.(\\d+)",
688
+ "os_replacement": "BlackBerry OS"
689
+ },
690
+ {
691
+ "regex": "(Black[Bb]erry)[0-9a-z]+/(\\d+)\\.(\\d+)\\.(\\d+)(?:\\.(\\d+))?",
692
+ "os_replacement": "BlackBerry OS"
693
+ },
694
+ {
695
+ "regex": "(Black[Bb]erry).+Version/(\\d+)\\.(\\d+)\\.(\\d+)(?:\\.(\\d+))?",
696
+ "os_replacement": "BlackBerry OS"
697
+ },
698
+ {
699
+ "regex": "(RIM Tablet OS) (\\d+)\\.(\\d+)\\.(\\d+)",
700
+ "os_replacement": "BlackBerry Tablet OS"
701
+ },
702
+ {
703
+ "regex": "(Play[Bb]ook)",
704
+ "os_replacement": "BlackBerry Tablet OS"
705
+ },
706
+ {
707
+ "regex": "(Black[Bb]erry)",
708
+ "os_replacement": "BlackBerry OS"
709
+ },
710
+ {
711
+ "regex": "\\(Mobile;.+Firefox/\\d+\\.\\d+",
712
+ "os_replacement": "Firefox OS"
713
+ },
714
+ {
715
+ "regex": "(BREW)[ /](\\d+)\\.(\\d+)\\.(\\d+)"
716
+ },
717
+ {
718
+ "regex": "(BREW);"
719
+ },
720
+ {
721
+ "regex": "(Brew MP|BMP)[ /](\\d+)\\.(\\d+)\\.(\\d+)",
722
+ "os_replacement": "Brew MP"
723
+ },
724
+ {
725
+ "regex": "BMP;",
726
+ "os_replacement": "Brew MP"
727
+ },
728
+ {
729
+ "regex": "(GoogleTV) (\\d+)\\.(\\d+)\\.(\\d+)"
730
+ },
731
+ {
732
+ "regex": "(GoogleTV)/\\d+"
733
+ },
734
+ {
735
+ "regex": "(WebTV)/(\\d+).(\\d+)"
736
+ },
737
+ {
738
+ "regex": "(hpw|web)OS/(\\d+)\\.(\\d+)(?:\\.(\\d+))?",
739
+ "os_replacement": "webOS"
740
+ },
741
+ {
742
+ "regex": "(VRE);"
743
+ },
744
+ {
745
+ "regex": "(Fedora|Red Hat|PCLinuxOS)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)"
746
+ },
747
+ {
748
+ "regex": "(Red Hat|Puppy|PCLinuxOS)/(\\d+)\\.(\\d+)\\.(\\d+)"
749
+ },
750
+ {
751
+ "regex": "(Ubuntu|Kindle|Bada|Lubuntu|BackTrack|Red Hat|Slackware)/(\\d+)\\.(\\d+)"
752
+ },
753
+ {
754
+ "regex": "(Windows|OpenBSD|FreeBSD|NetBSD|Android|WeTab)"
755
+ },
756
+ {
757
+ "regex": "(Ubuntu|Kubuntu|Arch Linux|CentOS|Slackware|Gentoo|openSUSE|SUSE|Red Hat|Fedora|PCLinuxOS|Gentoo|Mageia)"
758
+ },
759
+ {
760
+ "regex": "(Linux)/(\\d+)\\.(\\d+)"
761
+ },
762
+ {
763
+ "regex": "(Linux|BSD)"
764
+ },
765
+ {
766
+ "regex": "SunOS",
767
+ "os_replacement": "Solaris"
768
+ }
769
+ ],
770
+ "device_parsers": [
771
+ {
772
+ "regex": "HTC ([A-Z][a-z0-9]+) Build",
773
+ "device_replacement": "HTC $1"
774
+ },
775
+ {
776
+ "regex": "HTC ([A-Z][a-z0-9 ]+) \\d+\\.\\d+\\.\\d+\\.\\d+",
777
+ "device_replacement": "HTC $1"
778
+ },
779
+ {
780
+ "regex": "HTC_Touch_([A-Za-z0-9]+)",
781
+ "device_replacement": "HTC Touch ($1)"
782
+ },
783
+ {
784
+ "regex": "USCCHTC(\\d+)",
785
+ "device_replacement": "HTC $1 (US Cellular)"
786
+ },
787
+ {
788
+ "regex": "Sprint APA(9292)",
789
+ "device_replacement": "HTC $1 (Sprint)"
790
+ },
791
+ {
792
+ "regex": "HTC ([A-Za-z0-9]+ [A-Z])",
793
+ "device_replacement": "HTC $1"
794
+ },
795
+ {
796
+ "regex": "HTC[-_/\\s]([A-Za-z0-9]+)",
797
+ "device_replacement": "HTC $1"
798
+ },
799
+ {
800
+ "regex": "(ADR[A-Za-z0-9]+)",
801
+ "device_replacement": "HTC $1"
802
+ },
803
+ {
804
+ "regex": "(HTC)"
805
+ },
806
+ {
807
+ "regex": "(QtCarBrowser)",
808
+ "device_replacement": "Tesla Model S"
809
+ },
810
+ {
811
+ "regex": "(SamsungSGHi560)",
812
+ "device_replacement": "Samsung SGHi560"
813
+ },
814
+ {
815
+ "regex": "SonyEricsson([A-Za-z0-9]+)/",
816
+ "device_replacement": "Ericsson $1"
817
+ },
818
+ {
819
+ "regex": "PLAYSTATION 3",
820
+ "device_replacement": "PlayStation 3"
821
+ },
822
+ {
823
+ "regex": "(PlayStation Portable)"
824
+ },
825
+ {
826
+ "regex": "(PlayStation Vita)"
827
+ },
828
+ {
829
+ "regex": "(KFOT Build)",
830
+ "device_replacement": "Kindle Fire"
831
+ },
832
+ {
833
+ "regex": "(KFTT Build)",
834
+ "device_replacement": "Kindle Fire HD"
835
+ },
836
+ {
837
+ "regex": "(KFJWI Build)",
838
+ "device_replacement": "Kindle Fire HD 8.9\" WiFi"
839
+ },
840
+ {
841
+ "regex": "(KFJWA Build)",
842
+ "device_replacement": "Kindle Fire HD 8.9\" 4G"
843
+ },
844
+ {
845
+ "regex": "(Kindle Fire)"
846
+ },
847
+ {
848
+ "regex": "(Kindle)"
849
+ },
850
+ {
851
+ "regex": "(Silk)/(\\d+)\\.(\\d+)(?:\\.([0-9\\-]+))?",
852
+ "device_replacement": "Kindle Fire"
853
+ },
854
+ {
855
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+; [A-Za-z]{2}\\-[A-Za-z]{2}; WOWMobile (.+) Build"
856
+ },
857
+ {
858
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+\\-update1; [A-Za-z]{2}\\-[A-Za-z]{2}; (.+) Build"
859
+ },
860
+ {
861
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+\\.[\\d]+; [A-Za-z]{2}\\-[A-Za-z]{2}; (.+) Build"
862
+ },
863
+ {
864
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+\\.[\\d]+;[A-Za-z]{2}\\-[A-Za-z]{2};(.+) Build"
865
+ },
866
+ {
867
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+; [A-Za-z]{2}\\-[A-Za-z]{2}; (.+) Build"
868
+ },
869
+ {
870
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+\\.[\\d]+; (.+) Build"
871
+ },
872
+ {
873
+ "regex": "Android[\\- ][\\d]+\\.[\\d]+; (.+) Build"
874
+ },
875
+ {
876
+ "regex": "NokiaN([0-9]+)",
877
+ "device_replacement": "Nokia N$1"
878
+ },
879
+ {
880
+ "regex": "NOKIA([A-Za-z0-9\\v-]+)",
881
+ "device_replacement": "Nokia $1"
882
+ },
883
+ {
884
+ "regex": "Nokia([A-Za-z0-9\\v-]+)",
885
+ "device_replacement": "Nokia $1"
886
+ },
887
+ {
888
+ "regex": "NOKIA ([A-Za-z0-9\\-]+)",
889
+ "device_replacement": "Nokia $1"
890
+ },
891
+ {
892
+ "regex": "Nokia ([A-Za-z0-9\\-]+)",
893
+ "device_replacement": "Nokia $1"
894
+ },
895
+ {
896
+ "regex": "Lumia ([A-Za-z0-9\\-]+)",
897
+ "device_replacement": "Lumia $1"
898
+ },
899
+ {
900
+ "regex": "Symbian",
901
+ "device_replacement": "Nokia"
902
+ },
903
+ {
904
+ "regex": "BB10; ([A-Za-z0-9\\- ]+)\\)",
905
+ "device_replacement": "BlackBerry $1"
906
+ },
907
+ {
908
+ "regex": "(PlayBook).+RIM Tablet OS",
909
+ "device_replacement": "BlackBerry Playbook"
910
+ },
911
+ {
912
+ "regex": "Black[Bb]erry ([0-9]+);",
913
+ "device_replacement": "BlackBerry $1"
914
+ },
915
+ {
916
+ "regex": "Black[Bb]erry([0-9]+)",
917
+ "device_replacement": "BlackBerry $1"
918
+ },
919
+ {
920
+ "regex": "Black[Bb]erry;",
921
+ "device_replacement": "BlackBerry"
922
+ },
923
+ {
924
+ "regex": "(Pre)/(\\d+)\\.(\\d+)",
925
+ "device_replacement": "Palm Pre"
926
+ },
927
+ {
928
+ "regex": "(Pixi)/(\\d+)\\.(\\d+)",
929
+ "device_replacement": "Palm Pixi"
930
+ },
931
+ {
932
+ "regex": "(Touch[Pp]ad)/(\\d+)\\.(\\d+)",
933
+ "device_replacement": "HP TouchPad"
934
+ },
935
+ {
936
+ "regex": "HPiPAQ([A-Za-z0-9]+)/(\\d+).(\\d+)",
937
+ "device_replacement": "HP iPAQ $1"
938
+ },
939
+ {
940
+ "regex": "Palm([A-Za-z0-9]+)",
941
+ "device_replacement": "Palm $1"
942
+ },
943
+ {
944
+ "regex": "Treo([A-Za-z0-9]+)",
945
+ "device_replacement": "Palm Treo $1"
946
+ },
947
+ {
948
+ "regex": "webOS.*(P160UNA)/(\\d+).(\\d+)",
949
+ "device_replacement": "HP Veer"
950
+ },
951
+ {
952
+ "regex": "(AppleTV)",
953
+ "device_replacement": "AppleTV"
954
+ },
955
+ {
956
+ "regex": "AdsBot-Google-Mobile",
957
+ "device_replacement": "Spider"
958
+ },
959
+ {
960
+ "regex": "Googlebot-Mobile/(\\d+).(\\d+)",
961
+ "device_replacement": "Spider"
962
+ },
963
+ {
964
+ "regex": "(iPad) Simulator;"
965
+ },
966
+ {
967
+ "regex": "(iPad);"
968
+ },
969
+ {
970
+ "regex": "(iPod);"
971
+ },
972
+ {
973
+ "regex": "(iPhone) Simulator;"
974
+ },
975
+ {
976
+ "regex": "(iPhone);"
977
+ },
978
+ {
979
+ "regex": "acer_([A-Za-z0-9]+)_",
980
+ "device_replacement": "Acer $1"
981
+ },
982
+ {
983
+ "regex": "acer_([A-Za-z0-9]+)_",
984
+ "device_replacement": "Acer $1"
985
+ },
986
+ {
987
+ "regex": "ALCATEL-([A-Za-z0-9]+)",
988
+ "device_replacement": "Alcatel $1"
989
+ },
990
+ {
991
+ "regex": "Alcatel-([A-Za-z0-9]+)",
992
+ "device_replacement": "Alcatel $1"
993
+ },
994
+ {
995
+ "regex": "Amoi\\-([A-Za-z0-9]+)",
996
+ "device_replacement": "Amoi $1"
997
+ },
998
+ {
999
+ "regex": "AMOI\\-([A-Za-z0-9]+)",
1000
+ "device_replacement": "Amoi $1"
1001
+ },
1002
+ {
1003
+ "regex": "Asus\\-([A-Za-z0-9]+)",
1004
+ "device_replacement": "Asus $1"
1005
+ },
1006
+ {
1007
+ "regex": "ASUS\\-([A-Za-z0-9]+)",
1008
+ "device_replacement": "Asus $1"
1009
+ },
1010
+ {
1011
+ "regex": "BIRD\\-([A-Za-z0-9]+)",
1012
+ "device_replacement": "Bird $1"
1013
+ },
1014
+ {
1015
+ "regex": "BIRD\\.([A-Za-z0-9]+)",
1016
+ "device_replacement": "Bird $1"
1017
+ },
1018
+ {
1019
+ "regex": "BIRD ([A-Za-z0-9]+)",
1020
+ "device_replacement": "Bird $1"
1021
+ },
1022
+ {
1023
+ "regex": "Dell ([A-Za-z0-9]+)",
1024
+ "device_replacement": "Dell $1"
1025
+ },
1026
+ {
1027
+ "regex": "DoCoMo/2\\.0 ([A-Za-z0-9]+)",
1028
+ "device_replacement": "DoCoMo $1"
1029
+ },
1030
+ {
1031
+ "regex": "([A-Za-z0-9]+)_W\\;FOMA",
1032
+ "device_replacement": "DoCoMo $1"
1033
+ },
1034
+ {
1035
+ "regex": "([A-Za-z0-9]+)\\;FOMA",
1036
+ "device_replacement": "DoCoMo $1"
1037
+ },
1038
+ {
1039
+ "regex": "Huawei([A-Za-z0-9]+)",
1040
+ "device_replacement": "Huawei $1"
1041
+ },
1042
+ {
1043
+ "regex": "HUAWEI-([A-Za-z0-9]+)",
1044
+ "device_replacement": "Huawei $1"
1045
+ },
1046
+ {
1047
+ "regex": "vodafone([A-Za-z0-9]+)",
1048
+ "device_replacement": "Huawei Vodafone $1"
1049
+ },
1050
+ {
1051
+ "regex": "i\\-mate ([A-Za-z0-9]+)",
1052
+ "device_replacement": "i-mate $1"
1053
+ },
1054
+ {
1055
+ "regex": "Kyocera\\-([A-Za-z0-9]+)",
1056
+ "device_replacement": "Kyocera $1"
1057
+ },
1058
+ {
1059
+ "regex": "KWC\\-([A-Za-z0-9]+)",
1060
+ "device_replacement": "Kyocera $1"
1061
+ },
1062
+ {
1063
+ "regex": "Lenovo\\-([A-Za-z0-9]+)",
1064
+ "device_replacement": "Lenovo $1"
1065
+ },
1066
+ {
1067
+ "regex": "Lenovo_([A-Za-z0-9]+)",
1068
+ "device_replacement": "Lenovo $1"
1069
+ },
1070
+ {
1071
+ "regex": "LG/([A-Za-z0-9]+)",
1072
+ "device_replacement": "LG $1"
1073
+ },
1074
+ {
1075
+ "regex": "LG-LG([A-Za-z0-9]+)",
1076
+ "device_replacement": "LG $1"
1077
+ },
1078
+ {
1079
+ "regex": "LGE-LG([A-Za-z0-9]+)",
1080
+ "device_replacement": "LG $1"
1081
+ },
1082
+ {
1083
+ "regex": "LGE VX([A-Za-z0-9]+)",
1084
+ "device_replacement": "LG $1"
1085
+ },
1086
+ {
1087
+ "regex": "LG ([A-Za-z0-9]+)",
1088
+ "device_replacement": "LG $1"
1089
+ },
1090
+ {
1091
+ "regex": "LGE LG\\-AX([A-Za-z0-9]+)",
1092
+ "device_replacement": "LG $1"
1093
+ },
1094
+ {
1095
+ "regex": "LG\\-([A-Za-z0-9]+)",
1096
+ "device_replacement": "LG $1"
1097
+ },
1098
+ {
1099
+ "regex": "LGE\\-([A-Za-z0-9]+)",
1100
+ "device_replacement": "LG $1"
1101
+ },
1102
+ {
1103
+ "regex": "LG([A-Za-z0-9]+)",
1104
+ "device_replacement": "LG $1"
1105
+ },
1106
+ {
1107
+ "regex": "(KIN)\\.One (\\d+)\\.(\\d+)",
1108
+ "device_replacement": "Microsoft $1"
1109
+ },
1110
+ {
1111
+ "regex": "(KIN)\\.Two (\\d+)\\.(\\d+)",
1112
+ "device_replacement": "Microsoft $1"
1113
+ },
1114
+ {
1115
+ "regex": "(Motorola)\\-([A-Za-z0-9]+)"
1116
+ },
1117
+ {
1118
+ "regex": "MOTO\\-([A-Za-z0-9]+)",
1119
+ "device_replacement": "Motorola $1"
1120
+ },
1121
+ {
1122
+ "regex": "MOT\\-([A-Za-z0-9]+)",
1123
+ "device_replacement": "Motorola $1"
1124
+ },
1125
+ {
1126
+ "regex": "(Nintendo WiiU)",
1127
+ "device_replacement": "Nintendo Wii U"
1128
+ },
1129
+ {
1130
+ "regex": "Nintendo (DS|3DS|DSi|Wii);",
1131
+ "device_replacement": "Nintendo $1"
1132
+ },
1133
+ {
1134
+ "regex": "Pantech([A-Za-z0-9]+)",
1135
+ "device_replacement": "Pantech $1"
1136
+ },
1137
+ {
1138
+ "regex": "Philips([A-Za-z0-9]+)",
1139
+ "device_replacement": "Philips $1"
1140
+ },
1141
+ {
1142
+ "regex": "Philips ([A-Za-z0-9]+)",
1143
+ "device_replacement": "Philips $1"
1144
+ },
1145
+ {
1146
+ "regex": "SAMSUNG-([A-Za-z0-9\\-]+)",
1147
+ "device_replacement": "Samsung $1"
1148
+ },
1149
+ {
1150
+ "regex": "SAMSUNG\\; ([A-Za-z0-9\\-]+)",
1151
+ "device_replacement": "Samsung $1"
1152
+ },
1153
+ {
1154
+ "regex": "Dreamcast",
1155
+ "device_replacement": "Sega Dreamcast"
1156
+ },
1157
+ {
1158
+ "regex": "Softbank/1\\.0/([A-Za-z0-9]+)",
1159
+ "device_replacement": "Softbank $1"
1160
+ },
1161
+ {
1162
+ "regex": "Softbank/2\\.0/([A-Za-z0-9]+)",
1163
+ "device_replacement": "Softbank $1"
1164
+ },
1165
+ {
1166
+ "regex": "(WebTV)/(\\d+).(\\d+)"
1167
+ },
1168
+ {
1169
+ "regex": "(hiptop|avantgo|plucker|xiino|blazer|elaine|up.browser|up.link|mmp|smartphone|midp|wap|vodafone|o2|pocket|mobile|pda)",
1170
+ "device_replacement": "Generic Smartphone"
1171
+ },
1172
+ {
1173
+ "regex": "^(1207|3gso|4thp|501i|502i|503i|504i|505i|506i|6310|6590|770s|802s|a wa|acer|acs\\-|airn|alav|asus|attw|au\\-m|aur |aus |abac|acoo|aiko|alco|alca|amoi|anex|anny|anyw|aptu|arch|argo|bell|bird|bw\\-n|bw\\-u|beck|benq|bilb|blac|c55/|cdm\\-|chtm|capi|comp|cond|craw|dall|dbte|dc\\-s|dica|ds\\-d|ds12|dait|devi|dmob|doco|dopo|el49|erk0|esl8|ez40|ez60|ez70|ezos|ezze|elai|emul|eric|ezwa|fake|fly\\-|fly_|g\\-mo|g1 u|g560|gf\\-5|grun|gene|go.w|good|grad|hcit|hd\\-m|hd\\-p|hd\\-t|hei\\-|hp i|hpip|hs\\-c|htc |htc\\-|htca|htcg)",
1174
+ "device_replacement": "Generic Feature Phone"
1175
+ },
1176
+ {
1177
+ "regex": "^(htcp|htcs|htct|htc_|haie|hita|huaw|hutc|i\\-20|i\\-go|i\\-ma|i230|iac|iac\\-|iac/|ig01|im1k|inno|iris|jata|java|kddi|kgt|kgt/|kpt |kwc\\-|klon|lexi|lg g|lg\\-a|lg\\-b|lg\\-c|lg\\-d|lg\\-f|lg\\-g|lg\\-k|lg\\-l|lg\\-m|lg\\-o|lg\\-p|lg\\-s|lg\\-t|lg\\-u|lg\\-w|lg/k|lg/l|lg/u|lg50|lg54|lge\\-|lge/|lynx|leno|m1\\-w|m3ga|m50/|maui|mc01|mc21|mcca|medi|meri|mio8|mioa|mo01|mo02|mode|modo|mot |mot\\-|mt50|mtp1|mtv |mate|maxo|merc|mits|mobi|motv|mozz|n100|n101|n102|n202|n203|n300|n302|n500|n502|n505|n700|n701|n710|nec\\-|nem\\-|newg|neon)",
1178
+ "device_replacement": "Generic Feature Phone"
1179
+ },
1180
+ {
1181
+ "regex": "^(netf|noki|nzph|o2 x|o2\\-x|opwv|owg1|opti|oran|ot\\-s|p800|pand|pg\\-1|pg\\-2|pg\\-3|pg\\-6|pg\\-8|pg\\-c|pg13|phil|pn\\-2|pt\\-g|palm|pana|pire|pock|pose|psio|qa\\-a|qc\\-2|qc\\-3|qc\\-5|qc\\-7|qc07|qc12|qc21|qc32|qc60|qci\\-|qwap|qtek|r380|r600|raks|rim9|rove|s55/|sage|sams|sc01|sch\\-|scp\\-|sdk/|se47|sec\\-|sec0|sec1|semc|sgh\\-|shar|sie\\-|sk\\-0|sl45|slid|smb3|smt5|sp01|sph\\-|spv |spv\\-|sy01|samm|sany|sava|scoo|send|siem|smar|smit|soft|sony|t\\-mo|t218|t250|t600|t610|t618|tcl\\-|tdg\\-|telm|tim\\-|ts70|tsm\\-|tsm3|tsm5|tx\\-9|tagt)",
1182
+ "device_replacement": "Generic Feature Phone"
1183
+ },
1184
+ {
1185
+ "regex": "^(talk|teli|topl|tosh|up.b|upg1|utst|v400|v750|veri|vk\\-v|vk40|vk50|vk52|vk53|vm40|vx98|virg|vite|voda|vulc|w3c |w3c\\-|wapj|wapp|wapu|wapm|wig |wapi|wapr|wapv|wapy|wapa|waps|wapt|winc|winw|wonu|x700|xda2|xdag|yas\\-|your|zte\\-|zeto|aste|audi|avan|blaz|brew|brvw|bumb|ccwa|cell|cldc|cmd\\-|dang|eml2|fetc|hipt|http|ibro|idea|ikom|ipaq|jbro|jemu|jigs|keji|kyoc|kyok|libw|m\\-cr|midp|mmef|moto|mwbp|mywa|newt|nok6|o2im|pant|pdxg|play|pluc|port|prox|rozo|sama|seri|smal|symb|treo|upsi|vx52|vx53|vx60|vx61|vx70|vx80|vx81|vx83|vx85|wap\\-|webc|whit|wmlb|xda\\-|xda_)",
1186
+ "device_replacement": "Generic Feature Phone"
1187
+ },
1188
+ {
1189
+ "regex": "(bot|borg|google(^tv)|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast\\-webcrawler|converacrawler|dataparksearch|findlinks|crawler)",
1190
+ "device_replacement": "Spider"
1191
+ }
1192
+ ]
1193
+ }
includes/admin/uaparser/uaparser-cli.php ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*!
4
+ * ua-parser-php CLI v2.1.1
5
+ *
6
+ * Copyright (c) 2012 Dave Olsen, http://dmolsen.com
7
+ * Licensed under the MIT license
8
+ *
9
+ * spyc-0.5, for loading the YAML, is licensed under the MIT license.
10
+ * Services_JSON, for loading the JSON in sub-PHP 5.2 installs, is licensed under the MIT license
11
+ *
12
+ * This is the CLI for ua-parser-php. The following commands are supported:
13
+ *
14
+ * php uaparser-cli.php
15
+ * Provides the usage information.
16
+ *
17
+ * php uaparser-cli.php [-p] [-j] "your user agent string"
18
+ * Parses a user agent string and dumps the results as a list.
19
+ * Use the -j flag to print the result as JSON.
20
+ * Use the -p flag to pretty print the JSON result when using PHP 5.4+.
21
+ *
22
+ * php uaparser-cli.php -g [-s] [-n]
23
+ * Fetches an updated YAML file for UAParser and overwrites the current JSON file.
24
+ * By default is verbose. Use -s to turn that feature off.
25
+ * By default creates a back-up. Use -n to turn that feature off.
26
+ *
27
+ * php uaparser-cli.php -c [-s] [-n]
28
+ * Converts an existing regexes.yaml file to a regexes.json file.
29
+ * By default is verbose. Use -s to turn that feature off.
30
+ * By default creates a back-up. Use -n to turn that feature off.
31
+ *
32
+ * php uaparser-cli.php -y
33
+ * Fetches an updated YAML file. If you need to add a new UA it's easier to edit
34
+ * the original YAML and then convert it. Warning: This method overwrites any
35
+ * existing regexes.yaml file.
36
+ *
37
+ * php uaparser-cli.php -l /path/to/apache/logfile
38
+ * Parses the supplied Apache log file to test UAParser.php. Saves the UA to a file
39
+ * when the UA or OS family aren't found or when the UA is listed as a generic
40
+ * smartphone or as a generic feature phone.
41
+ *
42
+ * Thanks to Marcus Bointon (https://github.com/Synchro) for getting this file started
43
+ * and adding the initial JSON parser for a UA string.
44
+ *
45
+ */
46
+
47
+ // define the base path for the file
48
+ $basePath = dirname(__FILE__).DIRECTORY_SEPARATOR;
49
+
50
+ // address 5.1 compatibility
51
+ if (!function_exists('json_decode') || !function_exists('json_encode')) {
52
+ require_once($basePath."lib/json/jsonwrapper.php");
53
+ }
54
+
55
+ // include the YAML library
56
+ require_once($basePath."lib/spyc-0.5/spyc.php");
57
+
58
+ // include UAParser.php and make sure to turn off the CLI error
59
+ require_once($basePath."uaparser.php");
60
+
61
+ // deal with timezone issues & logging
62
+ if (!ini_get('date.timezone')) {
63
+ date_default_timezone_set(@date_default_timezone_get());
64
+ }
65
+
66
+ /*
67
+ * Gets the latest user agent. Back-ups the old version first. it will fail silently if something is wrong...
68
+ */
69
+ function get($file,$silent,$nobackup,$basePath) {
70
+ if (ini_get('allow_url_fopen')) {
71
+ if ($data = @file_get_contents($file)) {
72
+ if (!$silent) { print "loading and converting YAML data...\n"; };
73
+ $data = Spyc::YAMLLoad($data);
74
+ $data = json_encode($data);
75
+ if (!$silent) { print "encoded as JSON...\n"; };
76
+ if (file_exists($basePath."resources/regexes.json")) {
77
+ if (!$nobackup) {
78
+ if (!$silent) { print("backing up old JSON file...\n"); }
79
+ if (!copy($basePath."resources/regexes.json", $basePath."resources/regexes.".date("Ymdhis").".json")) {
80
+ if (!$silent) { print("back-up failed...\n"); }
81
+ exit;
82
+ }
83
+ }
84
+ }
85
+ file_put_contents($basePath."resources/regexes.json", $data);
86
+ if (!$silent) { print("saved JSON file...\n"); }
87
+ } else {
88
+ if (!$silent) { print("failed to get the file...\n"); }
89
+ }
90
+ } else {
91
+ if (!$silent) { print("ERROR: the allow_url_fopen option is not enabled in php.ini. it needs to be set to 'On' for this feature to work...\n"); }
92
+ }
93
+ }
94
+
95
+ /*
96
+ * Main logic for the CLI for the parser
97
+ */
98
+ if (php_sapi_name() == 'cli') {
99
+
100
+ // define the supported argument flags
101
+ $args = getopt("gsncyl:pj:");
102
+
103
+ // process the arguments
104
+ if (isset($args["g"])) {
105
+
106
+ /* Get regexes.yaml from the repo and convert it to JSON */
107
+
108
+ // set-up some standard vars
109
+ $silent = isset($args["s"]) ? true : false;
110
+ $nobackup = isset($args["n"]) ? true : false;
111
+
112
+ // start chatty
113
+ if (!$silent) {
114
+ print "getting the YAML file from the repo...\n";
115
+ }
116
+
117
+ // get the file
118
+ get("https://raw.github.com/tobie/ua-parser/master/regexes.yaml",$silent,$nobackup,$basePath);
119
+
120
+ } else if (isset($args["c"])) {
121
+
122
+ /* Convert regexes.yaml to regexes.json */
123
+
124
+ // set-up some standard vars
125
+ $silent = isset($args["s"]) ? true : false;
126
+ $nobackup = isset($args["n"]) ? true : false;
127
+
128
+ // start chatty
129
+ if (!$silent) {
130
+ print "getting the old YAML file...\n";
131
+ }
132
+
133
+ // get the file
134
+ get($basePath."resources/regexes.yaml",$silent,$nobackup,$basePath);
135
+
136
+ } else if (isset($args["y"])) {
137
+
138
+ /* Grabs regexes.yaml from the repo and saves it */
139
+
140
+ if ($data = @file_get_contents("https://raw.github.com/tobie/ua-parser/master/regexes.yaml")) {
141
+ file_put_contents($basePath."resources/regexes.yaml", $data);
142
+ print("saved YAML file from the repo...\n");
143
+ } else {
144
+ print("failed to get the YAML file from the repo...\n");
145
+ }
146
+
147
+ } else if (isset($args["l"]) && $args["l"]) {
148
+
149
+ /* Parse the supplied Apache log file */
150
+
151
+ // load the parser
152
+ $parser = new UAParser;
153
+
154
+ // set-up some standard vars
155
+ $i = 0;
156
+ $output = "";
157
+ $saved = array();
158
+ $data = @fopen($args["l"], "r");
159
+
160
+ if ($data) {
161
+ $fp = fopen($basePath."log/results-".date("YmdHis").".txt", "w");
162
+ while (($line = fgets($data)) !== false) {
163
+ $failure = false;
164
+ $show = "";
165
+ $line = str_replace("\n","",$line);
166
+ preg_match("/^(\S+) (\S+) (\S+) \[([^:]+):(\d+:\d+:\d+) ([^\]]+)\] \"(\S+) (.*?) (\S+)\" (\S+) (\S+) (\".*?\") (\"(.*?)\")$/", $line, $items);
167
+ $ua = (isset($items[14])) ? $items[14] : "";
168
+ if (!empty($ua) && ($ua != "-")) {
169
+ $result = $parser->parse($ua);
170
+ if (($result->ua->family == "Other") && ($result->device->family != "Spider")) {
171
+ $output = "UA Not Found: ".$ua." [".$line."]\n";
172
+ $show = "U";
173
+ } else if (($result->os->family == "Other") && ($result->device->family != "Spider")) {
174
+ $output = "OS Not Found: ".$ua." [".$line."]\n";
175
+ $show = "O";
176
+ } else if ($result->device->family == "Generic Smartphone") {
177
+ $output = "GS: ".$ua." [".$line."]\n";
178
+ $show = "GS";
179
+ } else if ($result->device->family == "Generic Feature Phone") {
180
+ $output = "GFP: ".$ua." [".$line."]\n";
181
+ $show = "GFP";
182
+ }
183
+ if ((($show == "U") || ($show == "O") || ($show == "GS") || ($show == "GFP")) && !in_array($ua,$saved)) {
184
+ fwrite($fp, $output);
185
+ $saved[] = $ua;
186
+ print $show;
187
+ } else {
188
+ $i = ($i < 20) ? $i+1 : 0;
189
+ if ($i == 0) {
190
+ print ".";
191
+ }
192
+ }
193
+ }
194
+ }
195
+ if (!feof($data)) {
196
+ print "Error: unexpected fgets() fail\n";
197
+ }
198
+ fclose($fp);
199
+ fclose($data);
200
+ print "\ncompleted the evaluation of the log file at ".$args["l"]."\n";
201
+ } else {
202
+ print "unable to read the file at the supplied path...\n";
203
+ }
204
+
205
+ } else if (isset($args["j"]) && $args["j"]) {
206
+
207
+ /* Parse the supplied UA from the command line and kick it out as JSON */
208
+
209
+ // load the parser
210
+ $parser = new UAParser;
211
+
212
+ // parse and encode the results
213
+ if (version_compare(PHP_VERSION, '5.4.0', '>=') && isset($args["p"])) {
214
+ print json_encode($parser->parse($args["j"]), JSON_PRETTY_PRINT);
215
+ } else {
216
+ print json_encode($parser->parse($args["j"]));
217
+ }
218
+ print PHP_EOL;
219
+
220
+ } else if (isset($argv[1]) && (($argv[1] != "-j") && ($argv[1] != "-p") && ($argv[1] != "-l") && ($argv[1] != "-s") && ($argv[1] != "-n"))) {
221
+
222
+ /* Parse the supplied UA from the command line and kick it out as JSON */
223
+
224
+ // load the parser
225
+ $parser = new UAParser;
226
+
227
+ // parse and print the results
228
+ $result = $parser->parse($argv[1]);
229
+ print " ua-parser results for \"".$argv[1]."\"\n";
230
+ foreach ($result as $key => $value) {
231
+ if (gettype($value) == "object") {
232
+ foreach ($value as $key2 => $value2) {
233
+ print " ".$key."->".$key2.": ".$value2."\n";
234
+ }
235
+ } else {
236
+ print " ".$key.": ".$value."\n";
237
+ }
238
+ }
239
+
240
+ } else {
241
+
242
+ /* Print usage information */
243
+
244
+ print "\n";
245
+ print "Usage:\n";
246
+ print "\n";
247
+ print " php uaparser-cli.php [-p] [-j] \"your user agent string\"\n";
248
+ print " Parses a user agent string and dumps the results as a list.\n";
249
+ print " Use the -j flag to print the result as JSON.\n";
250
+ print " Use the -p flag to pretty print the JSON result when using PHP 5.4+.\n";
251
+ print "\n";
252
+ print " php uaparser-cli.php -g [-s] [-n]\n";
253
+ print " Fetches an updated YAML file for ua-parser and overwrites the current JSON file.\n";
254
+ print " By default is verbose. Use -s to turn that feature off.\n";
255
+ print " By default creates a back-up. Use -n to turn that feature off.\n";
256
+ print "\n";
257
+ print " php uaparser-cli.php -c [-s] [-n]\n";
258
+ print " Converts an existing regexes.yaml file to a regexes.json file.\n";
259
+ print " By default is verbose. Use -s to turn that feature off.\n";
260
+ print " By default creates a back-up. Use -n to turn that feature off.\n";
261
+ print "\n";
262
+ print " php uaparser-cli.php -y\n";
263
+ print " Fetches an updated YAML file. If you need to add a new UA it's easier to edit\n";
264
+ print " the original YAML and then convert it. Warning: This method overwrites any\n";
265
+ print " existing regexes.yaml file.\n";
266
+ print "\n";
267
+ print " php uaparser-cli.php -l \"/path/to/apache/logfile\"\n";
268
+ print " Parses the supplied Apache log file to test UAParser.php. Saves the UA to a file\n";
269
+ print " when the UA or OS family aren't found or when the UA is listed as a generic\n";
270
+ print " smartphone or as a generic feature phone.\n";
271
+ print "\n";
272
+
273
+ }
274
+
275
+ } else {
276
+
277
+ print "You must run this file from the command line.";
278
+
279
+ }
includes/admin/uaparser/uaparser-test.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*!
4
+ * ua-parser-php Test Suite v2.1.1
5
+ *
6
+ * Copyright (c) 2012 Dave Olsen, http://dmolsen.com
7
+ * Licensed under the MIT license
8
+ *
9
+ * spyc-0.5, for loading the YAML, is licensed under the MIT license.
10
+ *
11
+ * This is the test suit for ua-parser-php to make sure it matches
12
+ * the standards set forth for ua-parser libraries.
13
+ *
14
+ * IMPORTANT: This test suite skips the Chrome Frame tests because
15
+ * the PHP lib doesn't support that feature.
16
+ *
17
+ */
18
+
19
+ // define the base path for the file
20
+ $basePath = dirname(__FILE__).DIRECTORY_SEPARATOR;
21
+
22
+ // include the YAML library
23
+ require_once($basePath."lib/spyc-0.5/spyc.php");
24
+
25
+ // include UAParser.php
26
+ require_once($basePath."uaparser.php");
27
+
28
+ // set-up the parser
29
+ $parser = new UAParser;
30
+
31
+ /**
32
+ * assert that the actual result and the expected result match
33
+ * NOTE: the lib calls for certain attributes to be set to null but the test cases are empty strings
34
+ * @param any the actual result of the parsing
35
+ * @param any the expected result of the test
36
+ * @return boolean the result of the test
37
+ */
38
+ function assertEqual($actual,$expected) {
39
+ $actual = ($actual == null) ? '' : $actual;
40
+ $result = ($actual === $expected) ? true : false;
41
+ return $result;
42
+ }
43
+
44
+ /**
45
+ * reports the mismatch between a test and what was returned from uaparser.php
46
+ * @param object the result of parsing the supplied test UA
47
+ * @param array the test case properties
48
+ * @param string the name of the test file
49
+ * @return string error report for the failing test
50
+ */
51
+ function reportMismatch($obj,$tc,$tf) {
52
+ if ($tf == "test_device") {
53
+ $info = "mismatch: got d: ".$obj->device->family." and expected d: ".$tc['family'];
54
+ } else if (($tf == "test_user_agent_parser_os") || ($tf == "additional_os_tests")) {
55
+ $info = "mismatch: got f: ".$obj->os->family." ma: ".$obj->os->major." mi: ".$obj->os->minor." p: ".$obj->os->patch." pm: ".$obj->os->patch_minor." expected f: ".$tc['family']." ma: ".$tc['major']." mi: ".$tc['minor']." p: ".$tc['patch']." pm: ".$tc['patch_minor'];
56
+ } else {
57
+ $info = "mismatch: got f: ".$obj->ua->family." ma: ".$obj->ua->major." mi: ".$obj->ua->minor." p: ".$obj->ua->patch." expected f: ".$tc['family']." ma: ".$tc['major']." mi: ".$tc['minor']." p: ".$tc['patch'];
58
+ }
59
+ print "\n ".$info;
60
+ print "\n the mismatched ua: ".$tc['user_agent_string'];
61
+ print "\n";
62
+ }
63
+
64
+ /*
65
+ * Main logic for the test suite
66
+ */
67
+
68
+ if (php_sapi_name() == "cli") {
69
+
70
+ if (!is_dir("../test_resources")) {
71
+ print "ERROR: the full ua-parser project needs to be loaded for the test suite to work. sorry.\n";
72
+ exit;
73
+ }
74
+
75
+ // test files and properties normally in them
76
+ $test_files = array("test_user_agent_parser","test_user_agent_parser_os","additional_os_tests","test_device","firefox_user_agent_strings");
77
+ $test_props = array("family", "major", "minor", "patch", "patch_minor");
78
+ $test_types = array("test_device" => "device", "test_user_agent_parser_os" => "os", "additional_os_tests" => "os", "test_user_agent_parser" => "ua", "firefox_user_agent_strings" => "ua");
79
+
80
+ foreach($test_files as $test_file) {
81
+ print "\n\nrunning uaparser.php against ".$test_file.".yaml...\n";
82
+ $data = Spyc::YAMLLoad($basePath."../test_resources/".$test_file.".yaml");
83
+ foreach($data["test_cases"] as $test_case) {
84
+ if (!isset($test_case["js_ua"])) {
85
+ $bool = true;
86
+ $result = $parser->parse($test_case["user_agent_string"]);
87
+ foreach ($test_props as $test_prop) {
88
+ if ($bool) {
89
+ $bool = isset($test_case[$test_prop]) ? assertEqual($result->$test_types[$test_file]->$test_prop,$test_case[$test_prop]) : $bool;
90
+ }
91
+ }
92
+ if (!$bool) {
93
+ reportMismatch($result,$test_case,$test_file);
94
+ } else {
95
+ print ".";
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ print "\n\ndone testing...\n";
102
+ } else {
103
+ print "You must run this file from the command line.";
104
+ }
includes/admin/uaparser/uaparser.php ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*!
4
+ * ua-parser-php v2.1.1
5
+ *
6
+ * Copyright (c) 2011-2012 Dave Olsen, http://dmolsen.com
7
+ * Licensed under the MIT license
8
+ *
9
+ * ua-parser-php is the PHP library for the ua-parser project. Learn more about the ua-parser project at:
10
+ *
11
+ * https://github.com/tobie/ua-parser
12
+ *
13
+ * The user agents data from the ua-parser project is licensed under the Apache license.
14
+ * spyc-0.5, for loading the YAML, is licensed under the MIT license.
15
+ * Services_JSON, for loading the JSON in sub-PHP 5.2 installs, is licensed under the MIT license
16
+ * The initial list of generic feature phones & smartphones came from Mobile Web OSP under the MIT license
17
+ * The initial list of spiders was taken from Yiibu's profile project under the MIT license.
18
+ *
19
+ * Many thanks to the following major contributors:
20
+ *
21
+ * - Bryan Shelton
22
+ * - Michael Bond
23
+ * - @rjd22 (https://github.com/rjd22)
24
+ * - Timo Tijhof (https://github.com/Krinkle)
25
+ * - Marcus Bointon (https://github.com/Synchro)
26
+ * - Ryan Parman (https://github.com/skyzyx)
27
+ * - Pravin Dahal (https://github.com/pravindahal)
28
+ */
29
+
30
+ // address 5.1 compatibility
31
+ if (!function_exists('json_decode') || !function_exists('json_encode')) {
32
+ require_once(dirname(__FILE__).DIRECTORY_SEPARATOR.'lib/json/jsonwrapper.php');
33
+ }
34
+
35
+ class UAParser {
36
+
37
+ protected $regexes;
38
+ protected $log = false;
39
+
40
+ /**
41
+ * Start up the parser by importing the json file to $this->regexes
42
+ */
43
+ public function __construct($customRegexesFile = null) {
44
+
45
+ $regexesFile = ($customRegexesFile !== null) ? $customRegexesFile : dirname(__FILE__).DIRECTORY_SEPARATOR.'resources/regexes.json';
46
+ if (file_exists($regexesFile)) {
47
+ $this->regexes = json_decode(file_get_contents($regexesFile));
48
+ } else {
49
+ $title = 'Error loading ua-parser';
50
+ if ($customRegexesFile !== null) {
51
+ $message = 'ua-parser can\'t find the custom regexes file you supplied ('.$customRegexesFile.'). Please make sure you have the correct path.';
52
+ $instruction1 = '';
53
+ $instruction2 = '';
54
+ } else {
55
+ $message = 'Please download the regexes.json file before using uaparser.php. You can type the following at the command line to download the latest version: ';
56
+ $instruction1 = '%: cd /path/to/UAParser/';
57
+ $instruction2 = '%: php uaparser-cli.php -g';
58
+ }
59
+
60
+ if (php_sapi_name() == 'cli') {
61
+ print "\n".$title."\n";
62
+ print $message."\n\n";
63
+ print " ".$instruction2."\n\n";
64
+ } else {
65
+ print '<html><head><title>'.$title.'</title></head><body>';
66
+ print '<h1>'.$title.'</h1>';
67
+ print '<p>'.$message.'</p>';
68
+ print '<blockquote>';
69
+ print '<code>'.$instruction1.'</code><br>';
70
+ print '<code>'.$instruction2.'</code>';
71
+ print '</blockquote>';
72
+ print '</body></html>';
73
+ }
74
+
75
+ exit;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Sets up some standard variables as well as starts the user agent parsing process
81
+ * @param string a user agent string to test, defaults to an empty string
82
+ * @return object the result of the user agent parsing
83
+ */
84
+ public function parse($ua = '') {
85
+
86
+ // build the default obj that will be returned
87
+ $result = (object) array(
88
+ 'ua' => (object) array(),
89
+ 'os' => (object) array(),
90
+ 'device' => (object) array(),
91
+ 'toFullString' => '',
92
+ 'uaOriginal' => $ua
93
+ );
94
+
95
+ // figure out the ua, os, and device properties if possible
96
+ $result->ua = $this->uaParse($ua);
97
+ $result->os = $this->osParse($ua);
98
+ $result->device = $this->deviceParse($ua);
99
+
100
+ // create a full string version based on the ua and os objects
101
+ $result->toFullString = $this->toFullString($result->ua, $result->os);
102
+
103
+ // log the results when testing
104
+ if ($this->log) {
105
+ $this->log($result);
106
+ }
107
+
108
+ return $result;
109
+
110
+ }
111
+
112
+ /**
113
+ * Attempts to see if the user agent matches a user_agents_parsers regex from regexes.json
114
+ * @param string a user agent string to test
115
+ * @return object the result of the user agent parsing
116
+ */
117
+ public function uaParse($uaString = '') {
118
+
119
+ // build the default obj that will be returned
120
+ $ua = (object) array(
121
+ 'family' => 'Other',
122
+ 'major' => null,
123
+ 'minor' => null,
124
+ 'patch' => null,
125
+ 'toString' => '',
126
+ 'toVersionString' => ''
127
+ );
128
+
129
+ // run the regexes to match things up
130
+ $uaRegexes = $this->regexes->user_agent_parsers;
131
+ foreach ($uaRegexes as $uaRegex) {
132
+
133
+ // tests the supplied regex against the user agent
134
+ if (preg_match('/'.str_replace('/','\/',str_replace('\/','/',$uaRegex->regex)).'/i',$uaString,$matches)) {
135
+
136
+ // Make sure matches are at least set to null or Other
137
+ if (!isset($matches[1])) { $matches[1] = 'Other'; }
138
+ if (!isset($matches[2])) { $matches[2] = null; }
139
+ if (!isset($matches[3])) { $matches[3] = null; }
140
+ if (!isset($matches[4])) { $matches[4] = null; }
141
+
142
+ // ua name
143
+ $ua->family = isset($uaRegex->family_replacement) ? str_replace('$1',$matches[1],$uaRegex->family_replacement) : $matches[1];
144
+
145
+ // version properties
146
+ $ua->major = isset($uaRegex->v1_replacement) ? $uaRegex->v1_replacement : $matches[2];
147
+ $ua->minor = isset($uaRegex->v2_replacement) ? $uaRegex->v2_replacement : $matches[3];
148
+ $ua->patch = isset($uaRegex->v3_replacement) ? $uaRegex->v3_replacement : $matches[4];
149
+
150
+ // extra strings
151
+ $ua->toString = $this->toString($ua);
152
+ $ua->toVersionString = $this->toVersionString($ua);
153
+
154
+ return $ua;
155
+ }
156
+
157
+ }
158
+
159
+ return $ua;
160
+
161
+ }
162
+
163
+ /**
164
+ * Attempts to see if the user agent matches an os_parsers regex from regexes.json
165
+ * @param string a user agent string to test
166
+ * @return object the result of the os parsing
167
+ */
168
+ public function osParse($uaString = '') {
169
+
170
+ // build the default obj that will be returned
171
+ $os = (object) array(
172
+ 'family' => 'Other',
173
+ 'major' => null,
174
+ 'minor' => null,
175
+ 'patch' => null,
176
+ 'patch_minor' => null,
177
+ 'toString' => '',
178
+ 'toVersionString' => ''
179
+ );
180
+
181
+ // run the regexes to match things up
182
+ $osRegexes = $this->regexes->os_parsers;
183
+ foreach ($osRegexes as $osRegex) {
184
+
185
+ if (preg_match('/'.str_replace('/','\/',str_replace('\/','/',$osRegex->regex)).'/i',$uaString,$matches)) {
186
+
187
+ // Make sure matches are at least set to null or Other
188
+ if (!isset($matches[1])) { $matches[1] = 'Other'; }
189
+ if (!isset($matches[2])) { $matches[2] = null; }
190
+ if (!isset($matches[3])) { $matches[3] = null; }
191
+ if (!isset($matches[4])) { $matches[4] = null; }
192
+ if (!isset($matches[5])) { $matches[5] = null; }
193
+
194
+ // os name
195
+ $os->family = isset($osRegex->os_replacement) ? $osRegex->os_replacement : $matches[1];
196
+
197
+ // version properties
198
+ $os->major = isset($osRegex->os_v1_replacement) ? $osRegex->os_v1_replacement : $matches[2];
199
+ $os->minor = isset($osRegex->os_v2_replacement) ? $osRegex->os_v2_replacement : $matches[3];
200
+ $os->patch = isset($osRegex->os_v3_replacement) ? $osRegex->os_v3_replacement : $matches[4];
201
+ $os->patch_minor = isset($osRegex->os_v4_replacement) ? $osRegex->os_v4_replacement : $matches[5];
202
+
203
+ // extra strings
204
+ $os->toString = $this->toString($os);
205
+ $os->toVersionString = $this->toVersionString($os);
206
+
207
+ return $os;
208
+ }
209
+
210
+ }
211
+
212
+ return $os;
213
+
214
+ }
215
+
216
+ /**
217
+ * Attempts to see if the user agent matches a device_parsers regex from regexes.json
218
+ * @param string a user agent string to test
219
+ * @return object the result of the device parsing
220
+ */
221
+ public function deviceParse($uaString = '') {
222
+
223
+ // build the default obj that will be returned
224
+ $device = (object) array(
225
+ 'family' => 'Other'
226
+ );
227
+
228
+ // run the regexes to match things up
229
+ $deviceRegexes = $this->regexes->device_parsers;
230
+ foreach ($deviceRegexes as $deviceRegex) {
231
+
232
+ if (preg_match('/'.str_replace('/','\/',str_replace('\/','/',$deviceRegex->regex)).'/i',$uaString,$matches)) {
233
+
234
+ // Make sure matches are at least set to null or Other
235
+ if (!isset($matches[1])) { $matches[1] = 'Other'; }
236
+
237
+ // device name
238
+ $device->family = isset($deviceRegex->device_replacement) ? str_replace('$1',str_replace("_"," ",$matches[1]),$deviceRegex->device_replacement) : str_replace("_"," ",$matches[1]);
239
+
240
+ return $device;
241
+
242
+ }
243
+
244
+ }
245
+
246
+ return $device;
247
+
248
+ }
249
+
250
+ /**
251
+ * Returns a string consisting of the family and full version number based on the provided type
252
+ * @param object the object (ua or os) to be used
253
+ * @return string the result of combining family and version
254
+ */
255
+ public function toString($obj) {
256
+
257
+ $versionString = $this->toVersionString($obj);
258
+ $string = !empty($versionString) ? $obj->family.' '.$versionString : $obj->family;
259
+
260
+ return $string;
261
+ }
262
+
263
+ /**
264
+ * Returns a string consisting of just the full version number based on the provided type
265
+ * @param object the obj that contains version number bits
266
+ * @return string the result of combining the version number bits together
267
+ */
268
+ public function toVersionString($obj) {
269
+
270
+ $versionString = isset($obj->major) ? $obj->major : '';
271
+ $versionString = isset($obj->minor) ? $versionString.'.'.$obj->minor : $versionString;
272
+ $versionString = isset($obj->patch) ? $versionString.'.'.$obj->patch : $versionString;
273
+ $versionString = isset($obj->patch_minor) ? $versionString.'.'.$obj->patch_minor : $versionString;
274
+
275
+ return $versionString;
276
+
277
+ }
278
+
279
+ /**
280
+ * Returns a string consistig of the family and full version number for both the browser and os
281
+ * @param object the ua object
282
+ * @param object the os object
283
+ * @return string the result of combining family and version
284
+ */
285
+ public function toFullString($ua,$os) {
286
+
287
+ $fullString = $this->toString($ua).'/'.$this->toString($os);
288
+
289
+ return $fullString;
290
+
291
+ }
292
+
293
+ /**
294
+ * Logs the user agent info
295
+ */
296
+ protected function log($data) {
297
+ $jsonData = json_encode($data);
298
+ $fp = fopen(dirname(__FILE__).DIRECTORY_SEPARATOR.'log/user_agents.log', 'a');
299
+ fwrite($fp, $jsonData."\r\n");
300
+ fclose($fp);
301
+ }
302
+
303
+ }
includes/class-dlm-ajax-handler.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * WP_DLM_Ajax_Handler class.
7
+ */
8
+ class WP_DLM_Ajax_Handler {
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function __construct() {
17
+ add_action( 'wp_ajax_download_monitor_remove_file', array( $this, 'remove_file' ) );
18
+ add_action( 'wp_ajax_download_monitor_add_file', array( $this, 'add_file' ) );
19
+ add_action( 'wp_ajax_download_monitor_list_files', array( $this, 'list_files' ) );
20
+ add_action( 'wp_ajax_download_monitor_insert_panel_upload', array( $this, 'insert_panel_upload' ) );
21
+ }
22
+
23
+ /**
24
+ * insert_panel_upload function.
25
+ *
26
+ * @access public
27
+ * @return void
28
+ */
29
+ public function insert_panel_upload() {
30
+
31
+ check_ajax_referer( 'file-upload' );
32
+
33
+ $status = wp_handle_upload( $_FILES['async-upload'], array( 'test_form' => false ) );
34
+
35
+ echo $status['url'];
36
+
37
+ die();
38
+ }
39
+
40
+ /**
41
+ * remove_file function.
42
+ *
43
+ * @access public
44
+ * @return void
45
+ */
46
+ public function remove_file() {
47
+
48
+ check_ajax_referer( 'remove-file', 'security' );
49
+
50
+ $file = get_post( intval( $_POST['file_id'] ) );
51
+
52
+ if ( $file && $file->post_type == "dlm_download_version" )
53
+ wp_delete_post( $file->ID );
54
+
55
+ die();
56
+ }
57
+
58
+ /**
59
+ * add_file function.
60
+ *
61
+ * @access public
62
+ * @return void
63
+ */
64
+ public function add_file() {
65
+
66
+ check_ajax_referer( 'add-file', 'security' );
67
+
68
+ $post_id = intval( $_POST['post_id'] );
69
+ $size = intval( $_POST['size'] );
70
+
71
+ $file = array(
72
+ 'post_title' => 'Download #' . $post_id . ' File Version',
73
+ 'post_content' => '',
74
+ 'post_status' => 'publish',
75
+ 'post_author' => get_current_user_id(),
76
+ 'post_parent' => $post_id,
77
+ 'post_type' => 'dlm_download_version'
78
+ );
79
+
80
+ $file_id = wp_insert_post( $file );
81
+ $i = $size;
82
+ $file_version = '';
83
+ $file_post_date = current_time( 'mysql' );
84
+ $file_download_count = 0;
85
+ $file_urls = array();
86
+
87
+ include( 'admin/html-downloadable-file-version.php' );
88
+
89
+ die();
90
+ }
91
+
92
+ /**
93
+ * list_files function.
94
+ *
95
+ * @access public
96
+ * @return void
97
+ */
98
+ public function list_files() {
99
+ global $download_monitor;
100
+
101
+ check_ajax_referer( 'list-files', 'security' );
102
+
103
+ if ( ! current_user_can('manage_downloads') ) return false;
104
+
105
+ $path = esc_attr( stripslashes( $_POST['path'] ) );
106
+
107
+ if ( $path ) {
108
+ $files = $download_monitor->list_files( $path );
109
+
110
+ foreach( $files as $found_file ) {
111
+
112
+ $file = pathinfo( $found_file['path'] );
113
+
114
+ if ( $found_file['type'] == 'folder' ) {
115
+
116
+ echo '<li><a href="#" class="folder" data-path="' . trailingslashit( $file['dirname'] ) . $file['basename'] . '">' . $file['basename'] . '</a></li>';
117
+
118
+ } else {
119
+
120
+ $filename = $file['basename'];
121
+ $extension = ( empty( $file['extension'] ) ) ? '' : $file['extension'];
122
+
123
+ if ( substr( $filename, 0, 1 ) == '.' ) continue; // Ignore files starting with . like htaccess
124
+ if ( in_array( $extension, array( '', 'php', 'html', 'htm', 'tmp' ) ) ) continue; // Ignored file types
125
+
126
+ echo '<li><a href="#" class="file filetype-' . sanitize_title( $extension ) . '" data-path="' . trailingslashit( $file['dirname'] ) . $file['basename'] . '">' . $file['basename'] . '</a></li>';
127
+
128
+ }
129
+
130
+ }
131
+ }
132
+
133
+ die();
134
+ }
135
+ }
136
+
137
+ new WP_DLM_Ajax_Handler();
includes/class-dlm-download-handler.php ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Download_Handler class.
7
+ */
8
+ class DLM_Download_Handler {
9
+
10
+ private $endpoint;
11
+ private $ep_value;
12
+
13
+ /**
14
+ * __construct function.
15
+ *
16
+ * @access public
17
+ * @return void
18
+ */
19
+ public function __construct() {
20
+ $this->endpoint = ( $endpoint = get_option( 'dlm_download_endpoint' ) ) ? $endpoint : 'download';
21
+ $this->ep_value = ( $ep_value = get_option( 'dlm_download_endpoint_value' ) ) ? $ep_value : 'ID';
22
+
23
+ add_filter( 'query_vars', array( $this, 'add_query_vars'), 0 );
24
+ add_action( 'init', array( $this, 'add_endpoint'), 0 );
25
+ add_action( 'parse_request', array( $this, 'handler'), 0 );
26
+ add_action( 'dlm_can_download', array( $this, 'check_access' ), 10, 2 );
27
+ }
28
+
29
+ /**
30
+ * Check access (hooked into dlm_can_download) checks if the download is members only and enfoces log in.
31
+ *
32
+ * Other plugins can use the 'dlm_can_download' filter directly to change access rights.
33
+ *
34
+ * @access public
35
+ * @param mixed $download
36
+ * @return void
37
+ */
38
+ public function check_access( $can_download, $download ) {
39
+ if ( $download->is_members_only() && ! is_user_logged_in() )
40
+ $can_download = false;
41
+
42
+ return $can_download;
43
+ }
44
+
45
+ /**
46
+ * add_query_vars function.
47
+ *
48
+ * @access public
49
+ * @return void
50
+ */
51
+ public function add_query_vars( $vars ) {
52
+ $vars[] = $this->endpoint;
53
+ return $vars;
54
+ }
55
+
56
+ /**
57
+ * add_endpoint function.
58
+ *
59
+ * @access public
60
+ * @return void
61
+ */
62
+ public function add_endpoint() {
63
+ add_rewrite_endpoint( $this->endpoint, EP_ALL );
64
+ }
65
+
66
+ /**
67
+ * Listen for download requets and trigger downloading.
68
+ *
69
+ * @access public
70
+ * @return void
71
+ */
72
+ public function handler() {
73
+ global $wp, $wpdb;
74
+
75
+ if ( ! empty( $_GET[ $this->endpoint ] ) )
76
+ $wp->query_vars[ $this->endpoint ] = $_GET[ $this->endpoint ];
77
+
78
+ if ( ! empty( $wp->query_vars[ $this->endpoint ] ) ) {
79
+
80
+ // Get ID of download
81
+ $raw_id = sanitize_title( stripslashes( $wp->query_vars[ $this->endpoint ] ) );
82
+
83
+ // Find real ID
84
+ switch ( $this->ep_value ) {
85
+ case 'slug' :
86
+ $download_id = absint( $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = '%s' AND post_type = 'dlm_download';", $raw_id ) ) );
87
+ break;
88
+ default :
89
+ $download_id = absint( $raw_id );
90
+ break;
91
+ }
92
+
93
+ if ( $download_id > 0 )
94
+ $download = new DLM_Download( $download_id );
95
+ else
96
+ $download = null;
97
+
98
+ // Handle version (if set)
99
+ $version_id = '';
100
+
101
+ if ( ! empty( $_GET['version'] ) ) {
102
+ $version_id = $download->get_version_id( $_GET['version'] );
103
+ }
104
+
105
+ if ( ! empty( $_GET['v'] ) ) {
106
+ $version_id = absint( $_GET['v'] );
107
+ }
108
+
109
+ if ( $version_id ) {
110
+ $download->set_version( $version_id );
111
+ }
112
+
113
+ // Action on found download
114
+ if ( ! is_null( $download ) && $download->exists() )
115
+ $this->trigger( $download, $version_id );
116
+
117
+ elseif ( $redirect = apply_filters( 'dlm_404_redirect', false ) )
118
+ wp_redirect( $redirect );
119
+
120
+ else
121
+ wp_die( __( 'Download does not exist.', 'download_monitor' ) . ' <a href="' . home_url() . '">' . __( 'Go to homepage &rarr;', 'download_monitor' ) . '</a>', __( 'Download Error', 'download_monitor' ) );
122
+
123
+ die('1');
124
+ }
125
+ }
126
+
127
+ /**
128
+ * trigger function.
129
+ *
130
+ * @access private
131
+ * @param mixed $download
132
+ * @return void
133
+ */
134
+ private function trigger( $download ) {
135
+
136
+ $version = $download->get_file_version();
137
+ $file_paths = $version->mirrors;
138
+
139
+ if ( empty( $file_paths ) )
140
+ wp_die( __( 'No file paths defined.', 'download_monitor' ) . ' <a href="' . home_url() . '">' . __( 'Go to homepage &rarr;', 'download_monitor' ) . '</a>', __( 'Download Error', 'download_monitor' ) );
141
+
142
+ $file_path = $file_paths[ array_rand( $file_paths ) ];
143
+
144
+ if ( ! $file_path )
145
+ wp_die( __( 'No file paths defined.', 'download_monitor' ) . ' <a href="' . home_url() . '">' . __( 'Go to homepage &rarr;', 'download_monitor' ) . '</a>', __( 'Download Error', 'download_monitor' ) );
146
+
147
+ // Check Access
148
+ if ( ! apply_filters( 'dlm_can_download', true, $download, $version ) ) {
149
+
150
+ if ( $redirect = apply_filters( 'dlm_access_denied_redirect', false ) )
151
+ wp_redirect( $redirect );
152
+
153
+ else
154
+ wp_die( __( 'You do not have permission to access this download.', 'download_monitor' ) . ' <a href="' . home_url() . '">' . __( 'Go to homepage &rarr;', 'download_monitor' ) . '</a>', __( 'Download Error', 'download_monitor' ) );
155
+
156
+ exit;
157
+ }
158
+
159
+ // Log Hit
160
+ $version->increase_download_count();
161
+
162
+ // Trigger Download Action
163
+ do_action( 'dlm_downloading', $download, $version );
164
+
165
+ // Redirect to the file...
166
+ if ( apply_filters( 'dlm_do_not_force', false, $download, $version ) ) {
167
+ if ( function_exists( 'dlm_create_log' ) )
168
+ dlm_create_log( 'download', 'redirected', __( 'Redirected to file', 'download_monitor' ), $download, $version );
169
+
170
+ header( 'Location: ' . $file_path );
171
+ exit;
172
+ }
173
+
174
+ // ...or serve it
175
+ if ( ! is_multisite() ) {
176
+
177
+ $file_path = str_replace( site_url( '/', 'https' ), ABSPATH, $file_path );
178
+ $file_path = str_replace( site_url( '/', 'http' ), ABSPATH, $file_path );
179
+
180
+ } else {
181
+
182
+ // Try to replace network url
183
+ $file_path = str_replace( network_admin_url( '/', 'https' ), ABSPATH, $file_path );
184
+ $file_path = str_replace( network_admin_url( '/', 'http' ), ABSPATH, $file_path );
185
+
186
+ // Try to replace upload URL
187
+ $upload_dir = wp_upload_dir();
188
+ $file_path = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $file_path );
189
+ }
190
+
191
+ // See if its local or remote
192
+ if ( strstr( $file_path, 'http:' ) || strstr( $file_path, 'https:' ) || strstr( $file_path, 'ftp:' ) ) {
193
+ $remote_file = true;
194
+ } else {
195
+ $remote_file = false;
196
+ $file_path = realpath( current( explode( '?', $file_path ) ) );
197
+ }
198
+
199
+ // Get Mime Type
200
+ $mime_type = "application/force-download";
201
+
202
+ foreach ( get_allowed_mime_types() as $mime => $type ) {
203
+ $mimes = explode( '|', $mime );
204
+ if ( in_array( $version->filetype, $mimes ) ) {
205
+ $mime_type = $type;
206
+ break;
207
+ }
208
+ }
209
+
210
+ // HEADERS
211
+ if ( ! ini_get('safe_mode') )
212
+ @set_time_limit(0);
213
+
214
+ if ( function_exists( 'get_magic_quotes_runtime' ) && get_magic_quotes_runtime() )
215
+ @set_magic_quotes_runtime(0);
216
+
217
+ if( function_exists( 'apache_setenv' ) )
218
+ @apache_setenv( 'no-gzip', 1 );
219
+
220
+ @session_write_close();
221
+ @ini_set( 'zlib.output_compression', 'Off' );
222
+ @ob_end_clean();
223
+
224
+ if ( ob_get_level() )
225
+ @ob_end_clean(); // Zip corruption fix
226
+
227
+ nocache_headers();
228
+
229
+ $file_name = basename( $file_path );
230
+
231
+ if ( strstr( $file_name, '?' ) )
232
+ $file_name = current( explode( '?', $file_name ) );
233
+
234
+ header( "Robots: none" );
235
+ header( "Content-Type: " . $mime_type );
236
+ header( "Content-Description: File Transfer" );
237
+ header( "Content-Disposition: attachment; filename=\"" . $file_name . "\";" );
238
+ header( "Content-Transfer-Encoding: binary" );
239
+
240
+ if ( $version->filesize )
241
+ header( "Content-Length: " . $version->filesize );
242
+
243
+ if ( get_option( 'dlm_xsendfile_enabled' ) ) {
244
+
245
+ if ( getcwd() )
246
+ $file_path = trim( preg_replace( '`^' . getcwd() . '`' , '', $file_path ), '/' );
247
+
248
+ header( "Content-Disposition: attachment; filename=\"" . $file_name . "\";" );
249
+
250
+ if ( function_exists( 'apache_get_modules' ) && in_array( 'mod_xsendfile', apache_get_modules() ) ) {
251
+ if ( function_exists( 'dlm_create_log' ) )
252
+ dlm_create_log( 'download', 'redirected', __( 'Redirected to file', 'download_monitor' ), $download, $version );
253
+
254
+ header("X-Sendfile: $file_path");
255
+ exit;
256
+ } elseif ( stristr( getenv( 'SERVER_SOFTWARE' ), 'lighttpd' ) ) {
257
+ if ( function_exists( 'dlm_create_log' ) )
258
+ dlm_create_log( 'download', 'redirected', __( 'Redirected to file', 'download_monitor' ), $download, $version );
259
+
260
+ header( "X-Lighttpd-Sendfile: $file_path" );
261
+ exit;
262
+ } elseif ( stristr( getenv( 'SERVER_SOFTWARE' ), 'nginx' ) || stristr( getenv( 'SERVER_SOFTWARE' ), 'cherokee' ) ) {
263
+ if ( function_exists( 'dlm_create_log' ) )
264
+ dlm_create_log( 'download', 'redirected', __( 'Redirected to file', 'download_monitor' ), $download, $version );
265
+
266
+ header( "X-Accel-Redirect: /$file_path" );
267
+ exit;
268
+ }
269
+ }
270
+
271
+ if ( $this->readfile_chunked( $file_path ) ) {
272
+ // Complete!
273
+ if ( function_exists( 'dlm_create_log' ) )
274
+ dlm_create_log( 'download', 'completed', '', $download, $version );
275
+
276
+ } elseif ( $remote_file ) {
277
+ // Redirect - we can't track if this completes or not
278
+ if ( function_exists( 'dlm_create_log' ) )
279
+ dlm_create_log( 'download', 'redirected', __( 'Redirected to remote file.', 'download_monitor' ), $download, $version );
280
+
281
+ header( 'Location: ' . $file_path );
282
+ } else {
283
+ if ( function_exists( 'dlm_create_log' ) )
284
+ dlm_create_log( 'download', 'failed', __( 'File not found', 'download_monitor' ), $download, $version );
285
+
286
+ wp_die( __( 'File not found.', 'download_monitor' ) . ' <a href="' . home_url() . '">' . __( 'Go to homepage &rarr;', 'download_monitor' ) . '</a>', __( 'Download Error', 'download_monitor' ) );
287
+ }
288
+
289
+ exit;
290
+ }
291
+
292
+ /**
293
+ * readfile_chunked
294
+ *
295
+ * Reads file in chunks so big downloads are possible without changing PHP.INI - http://codeigniter.com/wiki/Download_helper_for_large_files/
296
+ *
297
+ * @access public
298
+ * @param string file
299
+ * @param boolean return bytes of file
300
+ * @return void
301
+ */
302
+ public function readfile_chunked( $file, $retbytes = true ) {
303
+
304
+ $chunksize = 1 * ( 1024 * 1024 );
305
+ $buffer = '';
306
+ $cnt = 0;
307
+
308
+ $handle = @fopen( $file, 'r' );
309
+ if ( $handle === false )
310
+ return false;
311
+
312
+ while ( ! feof( $handle ) ) {
313
+ $buffer = fread( $handle, $chunksize );
314
+ echo $buffer;
315
+ ob_flush();
316
+ flush();
317
+
318
+ if ( $retbytes )
319
+ $cnt += strlen( $buffer );
320
+ }
321
+
322
+ $status = fclose( $handle );
323
+
324
+ if ( $retbytes && $status )
325
+ return $cnt;
326
+
327
+ return $status;
328
+ }
329
+ }
330
+
331
+ $GLOBALS['DLM_Download_Handler'] = new DLM_Download_Handler();
includes/class-dlm-download-version.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * DLM_Download_Version class.
5
+ */
6
+ class DLM_Download_Version {
7
+
8
+ /**
9
+ * __construct function.
10
+ *
11
+ * @access public
12
+ * @return void
13
+ */
14
+ public function __construct( $version_id, $download_id ) {
15
+ $this->id = absint( $version_id );
16
+ $this->download_id = absint( $download_id );
17
+
18
+ // Get Version Data
19
+ $this->mirrors = array_filter( (array) get_post_meta( $this->id, '_files', true ) );
20
+ $this->url = current( $this->mirrors );
21
+ $this->filename = current( explode( '?', basename( $this->url ) ) );
22
+ $this->filetype = strtolower( substr( strrchr( $this->filename, "." ), 1 ) );
23
+ $this->version = get_post_meta( $this->id, '_version', true );
24
+ $this->download_count = get_post_meta( $this->id, '_download_count', true );
25
+ $this->filesize = get_post_meta( $this->id, '_filesize', true );
26
+
27
+ // If data is not set, load it
28
+ if ( ! $this->filesize )
29
+ $this->filesize = $this->get_filesize( $this->url );
30
+ }
31
+
32
+ /**
33
+ * increase_download_count function.
34
+ *
35
+ * @access public
36
+ * @return void
37
+ */
38
+ public function increase_download_count() {
39
+ // File download_count
40
+ $this->download_count = absint( get_post_meta( $this->id, '_download_count', true ) ) + 1;
41
+ update_post_meta( $this->id, '_download_count', $this->download_count );
42
+
43
+ // Parent download download_count
44
+ $parent_download_count = absint( get_post_meta( $this->download_id, '_download_count', true ) ) + 1;
45
+ update_post_meta( $this->download_id, '_download_count', $parent_download_count );
46
+ }
47
+
48
+ /**
49
+ * get_filesize function.
50
+ *
51
+ * @access public
52
+ * @param mixed $file
53
+ * @return void
54
+ */
55
+ public function get_filesize( $file_path ) {
56
+
57
+ $filesize = null;
58
+
59
+ if ( ! is_multisite() ) {
60
+
61
+ $file_path = str_replace( site_url( '/', 'https' ), ABSPATH, $file_path );
62
+ $file_path = str_replace( site_url( '/', 'http' ), ABSPATH, $file_path );
63
+
64
+ } else {
65
+
66
+ // Try to replace network url
67
+ $file_path = str_replace( network_admin_url( '/', 'https' ), ABSPATH, $file_path );
68
+ $file_path = str_replace( network_admin_url( '/', 'http' ), ABSPATH, $file_path );
69
+
70
+ // Try to replace upload URL
71
+ $upload_dir = wp_upload_dir();
72
+ $file_path = str_replace( $upload_dir['baseurl'], $upload_dir['basedir'], $file_path );
73
+ }
74
+
75
+ if ( file_exists( $file_path ) && ( $filesize = filesize( $file_path ) ) )
76
+ $filesize = $filesize;
77
+ else {
78
+ $file = wp_remote_head( $file_path );
79
+
80
+ if ( ! is_wp_error( $file ) && ! empty( $file['headers']['content-length'] ) )
81
+ $filesize = $file['headers']['content-length'];
82
+ }
83
+
84
+ update_post_meta( $this->id, '_filesize', $filesize );
85
+
86
+ return $filesize;
87
+ }
88
+ }
includes/class-dlm-download.php ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * DLM_Download class.
4
+ */
5
+ class DLM_Download {
6
+
7
+ private $files;
8
+ private $file_version_ids;
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @param mixed $id
15
+ * @return void
16
+ */
17
+ public function __construct( $id ) {
18
+ $this->id = absint( $id );
19
+ $this->post = get_post( $this->id );
20
+ $this->version_id = ''; // Use latest current version
21
+ }
22
+
23
+ /**
24
+ * __isset function.
25
+ *
26
+ * @access public
27
+ * @param mixed $key
28
+ * @return bool
29
+ */
30
+ public function __isset( $key ) {
31
+ return metadata_exists( 'post', $this->id, '_' . $key );
32
+ }
33
+
34
+ /**
35
+ * __get function.
36
+ *
37
+ * @access public
38
+ * @param mixed $key
39
+ * @return mixed
40
+ */
41
+ public function __get( $key ) {
42
+
43
+ // Get values or default if not set
44
+ if ( 'members_only' == $key )
45
+ $value = ( $value = get_post_meta( $this->id, '_members_only', true ) ) ? $value : 'no';
46
+
47
+ elseif ( 'featured' == $key )
48
+ $value = ( $value = get_post_meta( $this->id, '_featured', true ) ) ? $value : 'no';
49
+
50
+ else
51
+ $value = get_post_meta( $this->id, '_' . $key, true );
52
+
53
+ return $value;
54
+ }
55
+
56
+ /**
57
+ * exists function.
58
+ *
59
+ * @access public
60
+ * @return void
61
+ */
62
+ public function exists() {
63
+ return ( ! is_null( $this->post ) );
64
+ }
65
+
66
+ /**
67
+ * version_exists function.
68
+ *
69
+ * @access public
70
+ * @param mixed $version_id
71
+ * @return void
72
+ */
73
+ public function version_exists( $version_id ) {
74
+ return in_array( $version_id, array_keys( $this->get_file_versions() ) );
75
+ }
76
+
77
+ /**
78
+ * Set the download to a version other than the current / latest version it defaults to.
79
+ *
80
+ * @access public
81
+ * @param mixed $version_id
82
+ * @return void
83
+ */
84
+ public function set_version( $version_id = '' ) {
85
+ if ( $this->version_exists( $version_id ) )
86
+ $this->version_id = $version_id;
87
+ else
88
+ $this->version_id = '';
89
+ }
90
+
91
+ /**
92
+ * get_title function.
93
+ *
94
+ * @access public
95
+ * @return void
96
+ */
97
+ public function get_the_title() {
98
+ return $this->post->post_title;
99
+ }
100
+
101
+ /**
102
+ * the_title function.
103
+ *
104
+ * @access public
105
+ * @return void
106
+ */
107
+ public function the_title() {
108
+ echo $this->get_the_title();
109
+ }
110
+
111
+ /**
112
+ * get_the_short_description function.
113
+ *
114
+ * @access public
115
+ * @return void
116
+ */
117
+ public function get_the_short_description() {
118
+ return wpautop( $this->post->post_excerpt );
119
+ }
120
+
121
+ /**
122
+ * the_short_description function.
123
+ *
124
+ * @access public
125
+ * @return void
126
+ */
127
+ public function the_short_description() {
128
+ echo $this->get_the_short_description();
129
+ }
130
+
131
+ /**
132
+ * get_the_image function.
133
+ *
134
+ * @access public
135
+ * @param string $size (default: 'full')
136
+ * @return void
137
+ */
138
+ public function get_the_image( $size = 'full' ) {
139
+ global $download_monitor;
140
+
141
+ if ( has_post_thumbnail( $this->id ) )
142
+ return get_the_post_thumbnail( $this->id, $size );
143
+ else
144
+ return '<img alt="Placeholder" class="wp-post-image" src="' . apply_filters( 'dlm_placeholder_image_src', $download_monitor->plugin_url() . '/assets/images/placeholder.png' ) . '" />';
145
+ }
146
+
147
+ /**
148
+ * the_image function.
149
+ *
150
+ * @access public
151
+ * @param string $size (default: 'full')
152
+ * @return void
153
+ */
154
+ public function the_image( $size = 'full' ) {
155
+ echo $this->get_the_image( $size );
156
+ }
157
+
158
+ /**
159
+ * get_author function.
160
+ *
161
+ * @access public
162
+ * @return void
163
+ */
164
+ public function get_the_author() {
165
+ $author_id = $this->post->post_author;
166
+ $user = get_user_by( 'ID', $author_id );
167
+ if ( $user )
168
+ return $user->display_name;
169
+ }
170
+
171
+ /**
172
+ * the_author function.
173
+ *
174
+ * @access public
175
+ * @return void
176
+ */
177
+ public function the_author() {
178
+ echo $this->get_the_author();
179
+ }
180
+
181
+ /**
182
+ * the_download_link function.
183
+ *
184
+ * @access public
185
+ * @return void
186
+ */
187
+ public function the_download_link() {
188
+ echo $this->get_the_download_link();
189
+ }
190
+
191
+ /**
192
+ * get_the_download_link function.
193
+ *
194
+ * @access public
195
+ * @return void
196
+ */
197
+ public function get_the_download_link() {
198
+ $scheme = parse_url( get_option( 'home' ), PHP_URL_SCHEME );
199
+ $endpoint = ( $endpoint = get_option( 'dlm_download_endpoint' ) ) ? $endpoint : 'download';
200
+ $ep_value = get_option( 'dlm_download_endpoint_value' );
201
+
202
+ switch ( $ep_value ) {
203
+ case 'slug' :
204
+ $value = $this->post->post_name;
205
+ break;
206
+ default :
207
+ $value = $this->id;
208
+ break;
209
+ }
210
+
211
+ if ( get_option('permalink_structure') ) {
212
+ $link = home_url( '/' . $endpoint . '/' . $value . '/', $scheme );
213
+ } else {
214
+ $link = add_query_arg( $endpoint, $value, home_url( '', $scheme ) );
215
+ }
216
+
217
+ if ( $this->version_id ) {
218
+
219
+ if ( $this->has_version_number() )
220
+ $link = add_query_arg( 'version', $this->get_the_version_number(), $link );
221
+ else
222
+ $link = add_query_arg( 'v', $this->version_id, $link );
223
+ }
224
+
225
+ return apply_filters( 'dlm_download_get_the_download_link', esc_url_raw( $link ), $this, $this->version_id );
226
+ }
227
+
228
+ /**
229
+ * the_download_count function.
230
+ *
231
+ * @access public
232
+ * @return void
233
+ */
234
+ public function the_download_count() {
235
+ echo $this->get_the_download_count();
236
+ }
237
+
238
+ /**
239
+ * get_the_download_count function.
240
+ *
241
+ * @access public
242
+ * @return void
243
+ */
244
+ public function get_the_download_count() {
245
+ if ( $this->version_id ) {
246
+ return absint( $this->get_file_version()->download_count );
247
+ } else {
248
+ return absint( $this->download_count );
249
+ }
250
+ }
251
+
252
+ /**
253
+ * has_version_number function.
254
+ *
255
+ * @access public
256
+ * @return void
257
+ */
258
+ public function has_version_number() {
259
+ return ! empty( $this->get_file_version()->version );
260
+ }
261
+
262
+ /**
263
+ * get_the_version_number function.
264
+ *
265
+ * @access public
266
+ * @return void
267
+ */
268
+ public function get_the_version_number() {
269
+ return ( $version = $this->get_file_version()->version ) ? $version : '1';
270
+ }
271
+
272
+ /**
273
+ * the_version_number function.
274
+ *
275
+ * @access public
276
+ * @return void
277
+ */
278
+ public function the_version_number() {
279
+ echo $this->get_the_version_number();
280
+ }
281
+
282
+ /**
283
+ * get_the_filename function.
284
+ *
285
+ * @access public
286
+ * @return void
287
+ */
288
+ public function get_the_filename() {
289
+ return $this->get_file_version()->filename;
290
+ }
291
+
292
+ /**
293
+ * the_filename function.
294
+ *
295
+ * @access public
296
+ * @return void
297
+ */
298
+ public function the_filename() {
299
+ echo $this->get_the_filename();
300
+ }
301
+
302
+ /**
303
+ * get_the_filesize function.
304
+ *
305
+ * @access public
306
+ * @return void
307
+ */
308
+ public function get_the_filesize() {
309
+ $filesize = $this->get_file_version()->filesize;
310
+
311
+ if ( $filesize )
312
+ return size_format( $filesize );
313
+ }
314
+
315
+ /**
316
+ * the_filesize function.
317
+ *
318
+ * @access public
319
+ * @return void
320
+ */
321
+ public function the_filesize() {
322
+ echo $this->get_the_filesize();
323
+ }
324
+
325
+ /**
326
+ * get_the_filetype function.
327
+ *
328
+ * @access public
329
+ * @return void
330
+ */
331
+ public function get_the_filetype() {
332
+ return $this->get_file_version()->filetype;
333
+ }
334
+
335
+ /**
336
+ * the_filetype function.
337
+ *
338
+ * @access public
339
+ * @return void
340
+ */
341
+ public function the_filetype() {
342
+ echo $this->get_the_filetype();
343
+ }
344
+
345
+ /**
346
+ * Get a version by ID, or default to current version.
347
+ *
348
+ * @access public
349
+ * @return void
350
+ */
351
+ public function get_file_version() {
352
+ $version = false;
353
+
354
+ if ( $this->version_id ) {
355
+ $versions = $this->get_file_versions();
356
+
357
+ if ( ! empty( $versions[ $this->version_id ] ) )
358
+ $version = $versions[ $this->version_id ];
359
+
360
+ } elseif ( $versions = $this->get_file_versions() ) {
361
+ $version = array_shift( $versions );
362
+ }
363
+
364
+ if ( ! $version ) {
365
+
366
+ $version = new stdClass();
367
+
368
+ $version->id = 0;
369
+ $version->download_id = $this->id;
370
+ $version->mirrors = array();
371
+ $version->url = '';
372
+ $version->filename = '';
373
+ $version->filetype = '';
374
+ $version->version = '';
375
+ $version->download_count = '';
376
+ $version->filesize = '';
377
+ }
378
+
379
+ return $version;
380
+ }
381
+
382
+ /**
383
+ * Get a version ID from a version string.
384
+ *
385
+ * @access public
386
+ * @return void
387
+ */
388
+ public function get_version_id( $version_string = '' ) {
389
+ $versions = $this->get_file_versions();
390
+
391
+ foreach ( $versions as $version_id => $version )
392
+ if ( version_compare( $version->version, $version_string, '=' ) )
393
+ return $version_id;
394
+ }
395
+
396
+ /**
397
+ * is_featured function.
398
+ *
399
+ * @access public
400
+ * @return bool
401
+ */
402
+ function is_featured() {
403
+ return ( $this->featured == 'yes' ) ? true : false;
404
+ }
405
+
406
+ /**
407
+ * is_members_only function.
408
+ *
409
+ * @access public
410
+ * @return bool
411
+ */
412
+ function is_members_only() {
413
+ return ( $this->members_only == 'yes' ) ? true : false;
414
+ }
415
+
416
+ /**
417
+ * get_file_version_ids function.
418
+ *
419
+ * @access public
420
+ * @return void
421
+ */
422
+ function get_file_version_ids() {
423
+ if ( ! is_array( $this->file_version_ids ) ) {
424
+
425
+ $this->file_version_ids = array();
426
+
427
+ $transient_name = 'dlm_children_ids_' . $this->id;
428
+
429
+ if ( false === ( $this->file_version_ids = get_transient( $transient_name ) ) ) {
430
+
431
+ $this->file_version_ids = get_posts( 'post_parent=' . $this->id . '&post_type=dlm_download_version&orderby=menu_order&order=ASC&fields=ids&post_status=publish&numberposts=-1' );
432
+
433
+ set_transient( $transient_name, $this->file_version_ids );
434
+ }
435
+ }
436
+
437
+ return $this->file_version_ids;
438
+ }
439
+
440
+ /**
441
+ * get_file_versions function.
442
+ *
443
+ * @access public
444
+ * @return void
445
+ */
446
+ public function get_file_versions() {
447
+ if ( $this->files )
448
+ return $this->files;
449
+
450
+ $version_ids = $this->get_file_version_ids();
451
+ $this->files = array();
452
+
453
+ foreach ( $version_ids as $version_id )
454
+ $this->files[ $version_id ] = new DLM_Download_Version( $version_id, $this->id );
455
+
456
+ return $this->files;
457
+ }
458
+ }
includes/class-dlm-logging.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Logging class.
7
+ */
8
+ class DLM_Logging {
9
+
10
+ /**
11
+ * create_log function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function create_log( $type, $status, $message, $download, $version ) {
17
+ global $wpdb;
18
+
19
+ $wpdb->insert(
20
+ $wpdb->download_log,
21
+ array(
22
+ 'type' => $type,
23
+ 'user_id' => absint( get_current_user_id() ),
24
+ 'user_ip' => $this->get_user_ip(),
25
+ 'user_agent' => $this->get_user_ua(),
26
+ 'download_id' => absint( $download->id ),
27
+ 'version_id' => absint( $version->id ),
28
+ 'version' => $version->version,
29
+ 'download_date' => current_time( 'mysql' ),
30
+ 'download_status' => $status,
31
+ 'download_status_message' => $message
32
+ ),
33
+ array(
34
+ '%s',
35
+ '%d',
36
+ '%s',
37
+ '%s',
38
+ '%d',
39
+ '%s',
40
+ '%s',
41
+ '%s',
42
+ '%s'
43
+ )
44
+ );
45
+
46
+ return $wpdb->insert_id;
47
+ }
48
+
49
+ /**
50
+ * get_user_ip function.
51
+ *
52
+ * @access private
53
+ * @return void
54
+ */
55
+ private function get_user_ip() {
56
+ return sanitize_text_field( ! empty( $_SERVER['HTTP_X_FORWARD_FOR'] ) ? $_SERVER['HTTP_X_FORWARD_FOR'] : $_SERVER['REMOTE_ADDR'] );
57
+ }
58
+
59
+ /**
60
+ * get_user_ua function.
61
+ *
62
+ * @access private
63
+ * @return void
64
+ */
65
+ private function get_user_ua() {
66
+ return sanitize_text_field( isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '' );
67
+ }
68
+ }
69
+
70
+ $GLOBALS['dlm_logging'] = new DLM_Logging();
71
+
72
+ /**
73
+ * dlm_create_log function.
74
+ *
75
+ * @access public
76
+ * @param string $type (default: '')
77
+ * @param string $status (default: '')
78
+ * @param string $message (default: '')
79
+ * @param mixed $download
80
+ * @param mixed $version
81
+ * @return void
82
+ */
83
+ function dlm_create_log( $type = '', $status = '', $message = '', $download, $version ) {
84
+ global $dlm_logging;
85
+
86
+ $dlm_logging->create_log( $type, $status, $message, $download, $version );
87
+ }
includes/class-dlm-shortcodes.php ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Shortcodes class.
7
+ */
8
+ class DLM_Shortcodes {
9
+
10
+ /**
11
+ * __construct function.
12
+ *
13
+ * @access public
14
+ * @return void
15
+ */
16
+ public function __construct() {
17
+ add_shortcode( 'total_downloads', array( $this, 'total_downloads' ) );
18
+ add_shortcode( 'total_files', array( $this, 'total_files' ) );
19
+ add_shortcode( 'download', array( $this, 'download' ) );
20
+ add_shortcode( 'download_data', array( $this, 'download_data' ) );
21
+ add_shortcode( 'downloads', array( $this, 'downloads' ) );
22
+ }
23
+
24
+ /**
25
+ * total_downloads function.
26
+ *
27
+ * @access public
28
+ * @return void
29
+ */
30
+ public function total_downloads() {
31
+ global $wpdb;
32
+
33
+ return $wpdb->get_var( "
34
+ SELECT SUM( meta_value ) FROM $wpdb->postmeta
35
+ LEFT JOIN $wpdb->posts on $wpdb->postmeta.post_id = $wpdb->posts.ID
36
+ WHERE meta_key = '_download_count'
37
+ AND post_type = 'dlm_download'
38
+ AND post_status = 'publish'
39
+ " );
40
+ }
41
+
42
+ /**
43
+ * total_files function.
44
+ *
45
+ * @access public
46
+ * @return void
47
+ */
48
+ public function total_files() {
49
+ $count_posts = wp_count_posts( 'dlm_download' );
50
+
51
+ return $count_posts->publish;
52
+ }
53
+
54
+ /**
55
+ * download function.
56
+ *
57
+ * @access public
58
+ * @param mixed $atts
59
+ * @return void
60
+ */
61
+ public function download( $atts ) {
62
+ global $download_monitor, $dlm_download;
63
+
64
+ extract( shortcode_atts( array(
65
+ 'id' => '',
66
+ 'autop' => false,
67
+ 'template' => get_option( 'dlm_default_template' ),
68
+ 'version_id' => '',
69
+ 'version' => ''
70
+ ), $atts ) );
71
+
72
+ $id = apply_filters( 'dlm_shortcode_download_id', $id );
73
+
74
+ if ( empty( $id ) )
75
+ return;
76
+
77
+ ob_start();
78
+
79
+ $downloads = new WP_Query( array(
80
+ 'post_type' => 'dlm_download',
81
+ 'posts_per_page' => 1,
82
+ 'no_found_rows' => 1,
83
+ 'post_status' => 'publish',
84
+ 'p' => $id
85
+ ) );
86
+
87
+ if ( $downloads->have_posts() ) {
88
+
89
+ while ( $downloads->have_posts() ) {
90
+ $downloads->the_post();
91
+
92
+ if ( $version )
93
+ $version_id = $dlm_download->get_version_id( $version );
94
+
95
+ if ( $version_id )
96
+ $dlm_download->set_version( $version_id );
97
+
98
+ $download_monitor->get_template_part( 'content-download', $template );
99
+ }
100
+
101
+ } else {
102
+ echo '[' . __( 'Download not found', 'download_monitor' ) . ']';
103
+ }
104
+
105
+ wp_reset_postdata();
106
+
107
+ if ( $autop === 'true' || $autop === true )
108
+ return wpautop( ob_get_clean() );
109
+ else
110
+ return ob_get_clean();
111
+ }
112
+
113
+ /**
114
+ * download_data function.
115
+ *
116
+ * @access public
117
+ * @param mixed $atts
118
+ * @param mixed $content
119
+ * @return void
120
+ */
121
+ public function download_data( $atts ) {
122
+ global $download_monitor;
123
+
124
+ extract( shortcode_atts( array(
125
+ 'id' => '',
126
+ 'data' => '',
127
+ 'version_id' => '',
128
+ 'version' => ''
129
+ ), $atts ) );
130
+
131
+ $id = apply_filters( 'dlm_shortcode_download_id', $id );
132
+
133
+ if ( empty( $id ) || empty( $data ) )
134
+ return;
135
+
136
+ $download = new DLM_Download( $id );
137
+
138
+ if ( $version )
139
+ $version_id = $download->get_version_id( $version );
140
+
141
+ if ( $version_id )
142
+ $download->set_version( $version_id );
143
+
144
+ switch ( $data ) {
145
+
146
+ // File / Version Info
147
+ case 'filename' :
148
+ return $download->get_the_filename();
149
+ case 'filetype' :
150
+ return $download->get_the_filetype();
151
+ case 'filesize' :
152
+ return $download->get_the_filesize();
153
+ case 'version' :
154
+ return $download->get_the_version_number();
155
+
156
+ // Download Info
157
+ case 'title' :
158
+ return $download->get_the_title();
159
+ case 'short_description' :
160
+ return $download->get_the_short_description();
161
+ case 'download_link' :
162
+ return $download->get_the_download_link();
163
+ case 'download_count' :
164
+ return $download->get_the_download_count();
165
+ case 'post_content' :
166
+ return wpautop( wptexturize( do_shortcode( $download->post->post_content ) ) );
167
+ case 'post_date' :
168
+ return date_i18n( get_option( 'date_format', $download->post->post_date ) );
169
+ case 'author' :
170
+ return $download->get_the_author();
171
+
172
+ // Images
173
+ case 'image' :
174
+ return $download->get_the_image( 'full' );
175
+ case 'thumbnail' :
176
+ return $download->get_the_image( 'thumbnail' );
177
+
178
+ // Taxonomies
179
+ case 'tags' :
180
+ return get_the_term_list( $id, 'dlm_download_tags', '', ', ', '' );
181
+ case 'categories' :
182
+ return get_the_term_list( $id, 'dlm_download_categories', '', ', ', '' );
183
+ }
184
+ }
185
+
186
+ /**
187
+ * downloads function.
188
+ *
189
+ * @access public
190
+ * @param mixed $atts
191
+ * @return void
192
+ */
193
+ public function downloads( $atts ) {
194
+ global $download_monitor;
195
+
196
+ extract( shortcode_atts( array(
197
+ // Query args
198
+ 'per_page' => '-1', // -1 = no limit
199
+ 'orderby' => 'title', // title, rand, ID, none, date, modifed, post__in, download_count
200
+ 'order' => 'desc', // ASC or DESC
201
+ 'include' => '', // Comma separate IDS
202
+ 'exclude' => '', // Comma separate IDS
203
+ 'offset' => '',
204
+ 'category' => '', // Comma separate slugs
205
+ 'tag' => '', // Comma separate slugs
206
+ 'featured' => false, // Set to true to only pull featured downloads
207
+ 'members_only' => false, // Set to true to only pull member downloads
208
+
209
+ // Output args
210
+ 'template' => get_option( 'dlm_default_template' ),
211
+ 'loop_start' => '<ul class="dlm-downloads">',
212
+ 'loop_end' => '</ul>',
213
+ 'before' => '<li>',
214
+ 'after' => '</li>'
215
+ ), $atts ) );
216
+
217
+ $post__in = ! empty( $include ) ? explode( ',', $include ) : '';
218
+ $post__not_in = ! empty( $exclude ) ? explode( ',', $exclude ) : '';
219
+ $order = strtoupper( $order );
220
+ $meta_key = '';
221
+
222
+ switch ( $orderby ) {
223
+ case 'title' :
224
+ case 'rand' :
225
+ case 'ID' :
226
+ case 'date' :
227
+ case 'modified' :
228
+ case 'post__in' :
229
+ $orderby = $orderby;
230
+ break;
231
+ case 'id' :
232
+ $orderby = 'ID';
233
+ break;
234
+ case 'hits' :
235
+ case 'count' :
236
+ case 'download_count' :
237
+ $orderby = 'meta_value';
238
+ $meta_key = '_download_count';
239
+ break;
240
+ default :
241
+ $orderby = 'title';
242
+ break;
243
+ }
244
+
245
+ $args = array(
246
+ 'post_type' => 'dlm_download',
247
+ 'posts_per_page' => $per_page,
248
+ 'offset' => $offset,
249
+ 'no_found_rows' => 1,
250
+ 'post_status' => 'publish',
251
+ 'orderby' => $orderby,
252
+ 'order' => $order,
253
+ '$meta_key' => $meta_key,
254
+ 'post__in' => $post__in,
255
+ 'post__not_in' => $post__not_in,
256
+ 'meta_query' => array()
257
+ );
258
+
259
+ if ( $category || $tag ) {
260
+ $args['tax_query'] = array( 'relation' => 'AND' );
261
+
262
+ $categories = array_filter( explode( ',', $category ) );
263
+ $tags = array_filter( explode( ',', $tag ) );
264
+
265
+ if ( ! empty( $categories ) ) {
266
+ $args['tax_query'][] = array(
267
+ 'taxonomy' => 'dlm_download_category',
268
+ 'field' => 'slug',
269
+ 'terms' => $categories
270
+ );
271
+ }
272
+
273
+ if ( ! empty( $tags ) ) {
274
+ $args['tax_query'][] = array(
275
+ 'taxonomy' => 'dlm_download_tag',
276
+ 'field' => 'slug',
277
+ 'terms' => $tags
278
+ );
279
+ }
280
+ }
281
+
282
+ if ( $featured === 'true' || $featured === true ) {
283
+ $args['meta_query'][] = array(
284
+ 'key' => '_featured',
285
+ 'value' => 'yes'
286
+ );
287
+ }
288
+
289
+ if ( $members_only === 'true' || $members_only === true ) {
290
+ $args['meta_query'][] = array(
291
+ 'key' => '_members_only',
292
+ 'value' => 'yes'
293
+ );
294
+ }
295
+
296
+ ob_start();
297
+
298
+ $downloads = new WP_Query( $args );
299
+
300
+ if ( $downloads->have_posts() ) : ?>
301
+
302
+ <?php echo html_entity_decode( $loop_start ); ?>
303
+
304
+ <?php while ( $downloads->have_posts() ) : $downloads->the_post(); ?>
305
+
306
+ <?php echo html_entity_decode( $before ); ?>
307
+
308
+ <?php $download_monitor->get_template_part( 'content-download', $template ); ?>
309
+
310
+ <?php echo html_entity_decode( $after ); ?>
311
+
312
+ <?php endwhile; // end of the loop. ?>
313
+
314
+ <?php echo html_entity_decode( $loop_end ); ?>
315
+
316
+ <?php endif;
317
+
318
+ wp_reset_postdata();
319
+
320
+ return ob_get_clean();
321
+ }
322
+ }
323
+
324
+ new DLM_Shortcodes();
includes/widgets/class-dlm-widget-downloads.php ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
4
+
5
+ /**
6
+ * DLM_Widget_Downloads class.
7
+ *
8
+ * @extends WP_Widget
9
+ */
10
+ class DLM_Widget_Downloads extends WP_Widget {
11
+
12
+ var $widget_cssclass;
13
+ var $widget_description;
14
+ var $widget_idbase;
15
+ var $widget_name;
16
+
17
+ /**
18
+ * constructor
19
+ *
20
+ * @access public
21
+ * @return void
22
+ */
23
+ function DLM_Widget_Downloads() {
24
+
25
+ /* Widget variable settings. */
26
+ $this->widget_cssclass = 'dlm_widget_downloads';
27
+ $this->widget_description = __( 'Display a list of your downloads.', 'download_monitor' );
28
+ $this->widget_idbase = 'dlm_widget_downloads';
29
+ $this->widget_name = __( 'Downloads List', 'download_monitor' );
30
+
31
+ /* Widget settings. */
32
+ $widget_ops = array( 'classname' => $this->widget_cssclass, 'description' => $this->widget_description );
33
+
34
+ /* Create the widget. */
35
+ $this->WP_Widget( 'dlm_widget_downloads', $this->widget_name, $widget_ops );
36
+ }
37
+
38
+ /**
39
+ * widget function.
40
+ *
41
+ * @see WP_Widget
42
+ * @access public
43
+ * @param array $args
44
+ * @param array $instance
45
+ * @return void
46
+ */
47
+ function widget( $args, $instance ) {
48
+ global $download_monitor;
49
+
50
+ extract( $args );
51
+
52
+ $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) : __( 'Featured Downloads', 'download_monitor' );
53
+ $posts_per_page = isset( $instance['posts_per_page'] ) ? absint( $instance['posts_per_page'] ) : 10;
54
+ $format = isset( $instance['format'] ) ? sanitize_title( $instance['format'] ) : '';
55
+ $orderby = isset( $instance['orderby'] ) ? $instance['orderby'] : 'title';
56
+ $order = isset( $instance['order'] ) ? $instance['order'] : 'ASC';
57
+ $featured = isset( $instance['featured'] ) ? $instance['featured'] : 'no';
58
+ $members_only = isset( $instance['members_only'] ) ? $instance['members_only'] : 'no';
59
+
60
+ $args = array(
61
+ 'post_status' => 'publish',
62
+ 'post_type' => 'dlm_download',
63
+ 'no_found_rows' => 1,
64
+ 'posts_per_page' => $posts_per_page,
65
+ 'orderby' => $orderby,
66
+ 'order' => $order,
67
+ 'meta_query' => array(),
68
+ 'tax_query' => array()
69
+ );
70
+
71
+ if ( $orderby == 'download_count' ) {
72
+ $args['orderby'] = 'meta_value';
73
+ $args['meta_key'] = '_download_count';
74
+ }
75
+
76
+ if ( $featured == 'yes' ) {
77
+ $args['meta_query'][] = array(
78
+ 'key' => '_featured',
79
+ 'value' => 'yes'
80
+ );
81
+ }
82
+
83
+ if ( $members_only == 'yes' ) {
84
+ $args['meta_query'][] = array(
85
+ 'key' => '_members_only',
86
+ 'value' => 'yes'
87
+ );
88
+ }
89
+
90
+ $r = new WP_Query( $args );
91
+
92
+ if ( $r->have_posts() ) {
93
+
94
+ echo $before_widget;
95
+
96
+ if ( $title )
97
+ echo $before_title . $title . $after_title;
98
+
99
+ echo apply_filters( 'dlm_widget_downloads_list_start', '<ul class="dlm-downloads">' );
100
+
101
+ while ( $r->have_posts()) {
102
+ $r->the_post();
103
+
104
+ echo apply_filters( 'dlm_widget_downloads_list_item_start', '<li>' );
105
+
106
+ $download_monitor->get_template_part( 'content-download', $format );
107
+
108
+ echo apply_filters( 'dlm_widget_downloads_list_item_end', '</li>' );
109
+ }
110
+
111
+ echo apply_filters( 'dlm_widget_downloads_list_end', '</ul>' );
112
+
113
+ echo $after_widget;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * update function.
119
+ *
120
+ * @see WP_Widget->update
121
+ * @access public
122
+ * @param array $new_instance
123
+ * @param array $old_instance
124
+ * @return array
125
+ */
126
+ function update( $new_instance, $old_instance ) {
127
+ $instance = $old_instance;
128
+ $instance['title'] = strip_tags( $new_instance['title'] );
129
+ $instance['posts_per_page'] = absint( $new_instance['posts_per_page'] );
130
+ $instance['format'] = sanitize_title( $new_instance['format'] );
131
+ $instance['orderby'] = sanitize_text_field( $new_instance['orderby'] );
132
+ $instance['order'] = sanitize_text_field( $new_instance['order'] );
133
+ $instance['featured'] = isset( $new_instance['featured'] ) ? 'yes' : 'no';
134
+ $instance['members_only'] = isset( $new_instance['members_only'] ) ? 'yes' : 'no';
135
+
136
+ return $instance;
137
+ }
138
+
139
+ /**
140
+ * form function.
141
+ *
142
+ * @see WP_Widget->form
143
+ * @access public
144
+ * @param array $instance
145
+ * @return void
146
+ */
147
+ function form( $instance ) {
148
+ $title = isset( $instance['title'] ) ? $instance['title'] : __( 'Featured Downloads', 'download_monitor' );
149
+ $posts_per_page = isset( $instance['posts_per_page'] ) ? absint( $instance['posts_per_page'] ) : 10;
150
+ $format = isset( $instance['format'] ) ? sanitize_title( $instance['format'] ) : '';
151
+ $orderby = isset( $instance['orderby'] ) ? $instance['orderby'] : 'title';
152
+ $order = isset( $instance['order'] ) ? $instance['order'] : 'ASC';
153
+ $featured = isset( $instance['featured'] ) ? $instance['featured'] : 'no';
154
+ $members_only = isset( $instance['members_only'] ) ? $instance['members_only'] : 'no';
155
+ ?>
156
+ <p>
157
+ <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:', 'download_monitor' ); ?></label>
158
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
159
+ </p>
160
+ <p>
161
+ <label for="<?php echo $this->get_field_id( 'posts_per_page' ); ?>"><?php _e( 'Limit:', 'download_monitor' ); ?></label>
162
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'posts_per_page' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'posts_per_page' ) ); ?>" type="text" value="<?php echo esc_attr( $posts_per_page ); ?>" size="3" />
163
+ </p>
164
+ <p>
165
+ <label for="<?php echo $this->get_field_id( 'format' ); ?>"><?php _e( 'Output template:', 'download_monitor' ); ?></label>
166
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'format' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'format' ) ); ?>" type="text" value="<?php echo esc_attr( $format ); ?>" placeholder="<?php _e( 'Default template', 'download_monitor' ); ?>" />
167
+ </p>
168
+ <p>
169
+ <label for="<?php echo $this->get_field_id( 'orderby' ); ?>"><?php _e( 'Order by:', 'download_monitor' ); ?></label>
170
+ <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'orderby' ) ); ?>" type="text">
171
+ <option value="title" <?php selected( $orderby, 'title' ); ?>><?php _e( 'Title', 'download_monitor' ); ?></option>
172
+ <option value="rand" <?php selected( $orderby, 'rand' ); ?>><?php _e( 'Random', 'download_monitor' ); ?></option>
173
+ <option value="ID" <?php selected( $orderby, 'ID' ); ?>><?php _e( 'ID', 'download_monitor' ); ?></option>
174
+ <option value="date" <?php selected( $orderby, 'date' ); ?>><?php _e( 'Date added', 'download_monitor' ); ?></option>
175
+ <option value="modified" <?php selected( $orderby, 'modified' ); ?>><?php _e( 'Date modified', 'download_monitor' ); ?></option>
176
+ <option value="download_count" <?php selected( $orderby, 'download_count' ); ?>><?php _e( 'Download count', 'download_monitor' ); ?></option>
177
+ </select>
178
+ </p>
179
+ <p>
180
+ <label for="<?php echo $this->get_field_id( 'order' ); ?>"><?php _e( 'Order:', 'download_monitor' ); ?></label>
181
+ <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'order' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'order' ) ); ?>" type="text">
182
+ <option value="ASC" <?php selected( $order, 'ASC' ); ?>><?php _e( 'ASC', 'download_monitor' ); ?></option>
183
+ <option value="DESC" <?php selected( $order, 'DESC' ); ?>><?php _e( 'DESC', 'download_monitor' ); ?></option>
184
+ </select>
185
+ </p>
186
+ <p>
187
+ <input id="<?php echo esc_attr( $this->get_field_id( 'featured' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'featured' ) ); ?>" type="checkbox" <?php checked( $featured, 'yes' ); ?> />
188
+ <label for="<?php echo $this->get_field_id( 'featured' ); ?>"><?php _e( 'Show only featured downloads', 'download_monitor' ); ?></label>
189
+ </p>
190
+ <p>
191
+ <input id="<?php echo esc_attr( $this->get_field_id( 'members_only' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'members_only' ) ); ?>" type="checkbox" <?php checked( $members_only, 'yes' ); ?> />
192
+ <label for="<?php echo $this->get_field_id( 'members_only' ); ?>"><?php _e( 'Show only members only downloads', 'download_monitor' ); ?></label>
193
+ </p>
194
+ <?php
195
+ }
196
+ }
readme.txt ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Download Monitor ===
2
+ Contributors: mikejolley
3
+ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=mike.jolley@me.com&item_name=Donation+for+Download+Monitor
4
+ Tags: download, downloads, monitor, hits, download monitor, tracking, admin, count, counter, files, versions, download count, logging
5
+ Requires at least: 3.5
6
+ Tested up to: 3.5
7
+ Stable tag: 1.0.0
8
+ License: GPLv3
9
+
10
+ Download Monitor is a plugin for uploading and managing downloads, tracking downloads, and displaying links.
11
+
12
+ == Description ==
13
+
14
+ Download Monitor provides an interface for uploading and managing downloadable files (including support for multiple versions), inserting download links into posts, and logging downloads.
15
+
16
+ = Features =
17
+
18
+ * Add, edit and remove downloads from a familiar WP interface; just like posts.
19
+ * Quick-add panel for adding files whilst editing posts.
20
+ * Add multiple file versions to your downloads.
21
+ * Define alternative links (mirrors) per version.
22
+ * Categorise, tag, or add other meta to your downloads.
23
+ * Display download links on the frontend using shortcodes.
24
+ * Change the way download links get displayed via template files.
25
+ * Track downloads counts and log user download attempts.
26
+ * Member only downloads.
27
+ * Customisable endpoints for showing pretty download links.
28
+
29
+ [Read more about Download Monitor](http://mikejolley.com/projects/download-monitor/).
30
+
31
+ = Documentation =
32
+
33
+ Documentation will be maintained on the [GitHub Wiki here](https://github.com/mikejolley/download-monitor/wiki).
34
+
35
+ = Add-ons =
36
+
37
+ Add-ons, such as the __legacy importer__ and __page addon__ are listed [found here](http://mikejolley.com/projects/download-monitor/add-ons/). Take a look!
38
+
39
+ = Contributing and reporting bugs =
40
+
41
+ You can contribute code and localizations to this plugin via GitHub: [https://github.com/mikejolley/download-monitor](https://github.com/mikejolley/download-monitor)
42
+
43
+ = Support =
44
+
45
+ Use the WordPress.org forums for community support - I cannot offer support directly for free. If you spot a bug, you can of course log it on [Github](https://github.com/mikejolley/download-monitor) instead where I can act upon it more efficiently.
46
+
47
+ If you want help with a customisation, hire a developer!
48
+
49
+ == Installation ==
50
+
51
+ = Automatic installation =
52
+
53
+ Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
54
+
55
+ In the search field type "Download Monitor" and click Search Plugins. Once you've found the plugin you can view details about it such as the the point release, rating and description. Most importantly of course, you can install it by clicking _Install Now_.
56
+
57
+ = Manual installation =
58
+
59
+ The manual installation method involves downloading the plugin and uploading it to your webserver via your favourite FTP application.
60
+
61
+ * Download the plugin file to your computer and unzip it
62
+ * Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's `wp-content/plugins/` directory.
63
+ * Activate the plugin from the Plugins menu within the WordPress admin.
64
+
65
+ == Frequently Asked Questions ==
66
+
67
+ = I used this before, so why is this version 1? =
68
+
69
+ Version 1.0.0 is a fresh start/complete rewrite of the legacy 3.0 version using modern best-practices such as custom post types and endpoints. Because of this, data from the legacy plugin won't work without migration using [the legacy importer](http://mikejolley.com/projects/download-monitor/add-ons/legacy-importer/). Since this upgrade process isn't straightforward nor automated I've reverted the version to 1.0.0 to prevent automatic updates.
70
+
71
+ Legacy versions can still be [found here](http://wordpress.org/plugins/download-monitor/developers/).
72
+
73
+ = X feature/shortcode is missing from the legacy version; why? =
74
+
75
+ The rewrite has trimmed the fat and only kept the best, most useful features. If something is missing, you can always code it yourself - the new system is very flexible and its easy to query files using [get_posts](http://codex.wordpress.org/Template_Tags/get_posts).
76
+
77
+ If you are missing the "Page Addon", this is now a separate plugin found here: [Download Monitor Page Addon](http://mikejolley.com/projects/download-monitor/add-ons/page-addon/).
78
+
79
+ = Can I upload .xxx filetype using the uploader? =
80
+
81
+ Download Monitor uses the WordPress uploader for uploading files. By default these formats are supported:
82
+
83
+ * Images - .jpg, .jpeg, .png, .gif
84
+ * Documents - .pdf, .doc, .docx, .ppt, .pptx, .pps, .ppsx, .odt, .xls, .xlsx
85
+ * Music - .mp3, .m4a, .ogg, .wav
86
+ * Video - .mp4, .m4v, .mov, .wmv, .avi, .mpg, .ogv, .3gp, .3g2
87
+
88
+ To add more you can use a plugin, or filters. This post is a good resource for doing it with filters: [Change WordPress Upload Mime Types](http://www.paulund.co.uk/change-wordpress-upload-mime-types).
89
+
90
+ = Can I link to external downloads? =
91
+
92
+ Yes, you can use both local paths and external URLs.
93
+
94
+ = My Download links 404 =
95
+
96
+ Download links are powered by endpoints. If you find them 404'ing, go to Settings > Permalinks and save. This will flush the permalinks and allow our endpoints to be added.
97
+
98
+ = Download counts are not increasing when I download something =
99
+
100
+ Admin hits are not counted, log out and try!
101
+
102
+ == Screenshots ==
103
+
104
+ 1. The main admin screen lists your downloads using familiar WordPress UI.
105
+ 2. Easily add file information and multiple versions.
106
+ 3. The quick add panel can be opened via a link about the post editor. This lets you quickly add a file and insert it into a post.
107
+ 4. Display regular download links or fancy ones all using shortcodes and templates.
108
+
109
+ == Changelog ==
110
+
111
+ = 1.0.0 =
112
+ * Complete rewrite of the plugin making use of custom post types and other best practices. Fresh start version '1' to prevent auto-updates (legacy importer needs to be used to migrate from old versions).
113
+
114
+ == Upgrade Notice ==
115
+
116
+ = 1.0.0 =
117
+ To update from legacy versions use the legacy importer](http://mikejolley.com/projects/download-monitor/add-ons/legacy-importer/).
templates/content-download-box.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Detailed download output
4
+ */
5
+
6
+ global $dlm_download;
7
+ ?>
8
+ <aside class="download-box">
9
+
10
+ <?php $dlm_download->the_image(); ?>
11
+
12
+ <div class="download-count"><?php printf( _n( '1 download', '%d downloads', $dlm_download->get_the_download_count(), 'download_monitor' ), $dlm_download->get_the_download_count() ) ?></div>
13
+
14
+ <div class="download-box-content">
15
+
16
+ <h1><?php $dlm_download->the_title(); ?></h1>
17
+
18
+ <?php $dlm_download->the_short_description(); ?>
19
+
20
+ <a class="download-button" title="<?php if ( $dlm_download->has_version_number() ) printf( __( 'Version %s', 'download_monitor' ), $dlm_download->get_the_version_number() ); ?>" href="<?php $dlm_download->the_download_link(); ?>" rel="nofollow">
21
+ <?php _e( 'Download File', 'download_monitor' ); ?>
22
+ <small><?php $dlm_download->the_filename(); ?> &ndash; <?php $dlm_download->the_filesize(); ?></small>
23
+ </a>
24
+
25
+ </div>
26
+ </aside>
27
+
28
+
templates/content-download-button.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Download button
4
+ */
5
+
6
+ global $dlm_download, $download_monitor;
7
+ ?>
8
+ <p><a class="aligncenter download-button" href="<?php $dlm_download->the_download_link(); ?>" rel="nofollow">
9
+ <?php printf( __( 'Download &ldquo;%s&rdquo;', 'download_monitor' ), $dlm_download->get_the_title() ); ?>
10
+ <small><?php $dlm_download->the_filename(); ?> &ndash; <?php printf( _n( 'Downloaded 1 time', 'Downloaded %d times', $dlm_download->get_the_download_count(), 'download_monitor' ), $dlm_download->get_the_download_count() ) ?> &ndash; <?php $dlm_download->the_filesize(); ?></small>
11
+ </a></p>
templates/content-download-filename.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default output for a download via the [download] shortcode
4
+ */
5
+
6
+ global $dlm_download;
7
+ ?>
8
+ <a class="download-link filetype-icon <?php echo 'filetype-' . $dlm_download->get_the_filetype(); ?>" title="<?php if ( $dlm_download->has_version_number() ) printf( __( 'Version %s', 'download_monitor' ), $dlm_download->get_the_version_number() ); ?>" href="<?php $dlm_download->the_download_link(); ?>" rel="nofollow">
9
+ <?php $dlm_download->the_filename(); ?> (<?php printf( _n( '1 download', '%d downloads', $dlm_download->get_the_download_count(), 'download_monitor' ), $dlm_download->get_the_download_count() ) ?>)
10
+ </a>
templates/content-download.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default output for a download via the [download] shortcode
4
+ */
5
+
6
+ global $dlm_download;
7
+ ?>
8
+ <a class="download-link" title="<?php if ( $dlm_download->has_version_number() ) printf( __( 'Version %s', 'download_monitor' ), $dlm_download->get_the_version_number() ); ?>" href="<?php $dlm_download->the_download_link(); ?>" rel="nofollow">
9
+ <?php $dlm_download->the_title(); ?> (<?php printf( _n( '1 download', '%d downloads', $dlm_download->get_the_download_count(), 'download_monitor' ), $dlm_download->get_the_download_count() ) ?>)
10
+ </a>