comfortable_media_surfer 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (491) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +4 -0
  3. data/.gitattributes +2 -0
  4. data/.github/issue_template.md +19 -0
  5. data/.github/pull_request_template.md +6 -0
  6. data/.github/workflows/coveralls.yml +40 -0
  7. data/.github/workflows/rubyonrails.yml +51 -0
  8. data/.gitignore +21 -0
  9. data/.rubocop.yml +75 -0
  10. data/.travis.yml +44 -0
  11. data/CONTRIBUTING.md +35 -0
  12. data/Gemfile +38 -0
  13. data/LICENSE +20 -0
  14. data/README.md +109 -0
  15. data/Rakefile +5 -0
  16. data/app/assets/config/manifest.js +0 -0
  17. data/app/assets/fonts/comfy/admin/cms/lib/redactor-font.eot +0 -0
  18. data/app/assets/javascripts/comfy/admin/cms/application.js +39 -0
  19. data/app/assets/javascripts/comfy/admin/cms/base.js +46 -0
  20. data/app/assets/javascripts/comfy/admin/cms/categories.js +17 -0
  21. data/app/assets/javascripts/comfy/admin/cms/codemirror.js +31 -0
  22. data/app/assets/javascripts/comfy/admin/cms/custom.js +1 -0
  23. data/app/assets/javascripts/comfy/admin/cms/diff.js +10 -0
  24. data/app/assets/javascripts/comfy/admin/cms/file_link.js +67 -0
  25. data/app/assets/javascripts/comfy/admin/cms/file_upload.js +194 -0
  26. data/app/assets/javascripts/comfy/admin/cms/files_modal.js +40 -0
  27. data/app/assets/javascripts/comfy/admin/cms/page_fragments.js +22 -0
  28. data/app/assets/javascripts/comfy/admin/cms/slugify.js +34 -0
  29. data/app/assets/javascripts/comfy/admin/cms/sortable_list.js +40 -0
  30. data/app/assets/javascripts/comfy/admin/cms/timepicker.js +30 -0
  31. data/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +72 -0
  32. data/app/assets/javascripts/comfy/vendor/Moxie.swf +0 -0
  33. data/app/assets/javascripts/comfy/vendor/Moxie.xap +0 -0
  34. data/app/assets/javascripts/comfy/vendor/bootstrap.bundle.min.js +7 -0
  35. data/app/assets/javascripts/comfy/vendor/bootstrap.bundle.min.js.map +1 -0
  36. data/app/assets/javascripts/comfy/vendor/codemirror/addon/edit/closetag.js +169 -0
  37. data/app/assets/javascripts/comfy/vendor/codemirror/mode/css/css.js +832 -0
  38. data/app/assets/javascripts/comfy/vendor/codemirror/mode/htmlmixed/htmlmixed.js +152 -0
  39. data/app/assets/javascripts/comfy/vendor/codemirror/mode/javascript/javascript.js +875 -0
  40. data/app/assets/javascripts/comfy/vendor/codemirror/mode/markdown/markdown.js +861 -0
  41. data/app/assets/javascripts/comfy/vendor/codemirror/mode/xml/xml.js +394 -0
  42. data/app/assets/javascripts/comfy/vendor/codemirror.js +9653 -0
  43. data/app/assets/javascripts/comfy/vendor/diff/diff_match_patch.min.js +49 -0
  44. data/app/assets/javascripts/comfy/vendor/diff/pretty_text_diff.js +76 -0
  45. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ar.js +52 -0
  46. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/bg.js +65 -0
  47. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/bn.js +65 -0
  48. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ca.js +83 -0
  49. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/cs.js +70 -0
  50. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/cy.js +92 -0
  51. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/da.js +71 -0
  52. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/de.js +70 -0
  53. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/default.js +80 -0
  54. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/eo.js +73 -0
  55. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/es.js +69 -0
  56. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/et.js +73 -0
  57. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/fa.js +68 -0
  58. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/fi.js +69 -0
  59. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/fr.js +75 -0
  60. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/gr.js +74 -0
  61. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/he.js +57 -0
  62. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/hi.js +65 -0
  63. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/hr.js +66 -0
  64. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/hu.js +72 -0
  65. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/id.js +61 -0
  66. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/it.js +70 -0
  67. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ja.js +57 -0
  68. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ko.js +60 -0
  69. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/lt.js +72 -0
  70. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/lv.js +67 -0
  71. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/mk.js +69 -0
  72. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/mn.js +67 -0
  73. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ms.js +68 -0
  74. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/my.js +69 -0
  75. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/nl.js +75 -0
  76. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/no.js +73 -0
  77. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pa.js +65 -0
  78. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pl.js +68 -0
  79. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pt-BR.js +66 -0
  80. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pt.js +66 -0
  81. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ro.js +69 -0
  82. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ru.js +66 -0
  83. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/si.js +65 -0
  84. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sk.js +70 -0
  85. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sl.js +70 -0
  86. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sq.js +65 -0
  87. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sr.js +69 -0
  88. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sv.js +70 -0
  89. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/th.js +72 -0
  90. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/tr.js +66 -0
  91. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/uk.js +66 -0
  92. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/vn.js +66 -0
  93. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/zh-CN.js +61 -0
  94. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/zh-TW.js +61 -0
  95. data/app/assets/javascripts/comfy/vendor/flatpickr.min.js +2 -0
  96. data/app/assets/javascripts/comfy/vendor/fontawesome.js +5 -0
  97. data/app/assets/javascripts/comfy/vendor/moxie.min.js +16 -0
  98. data/app/assets/javascripts/comfy/vendor/plupload.dev.js +2497 -0
  99. data/app/assets/javascripts/comfy/vendor/redactor/definedlinks.js +53 -0
  100. data/app/assets/javascripts/comfy/vendor/redactor/filemanager.js +64 -0
  101. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ar.js +76 -0
  102. data/app/assets/javascripts/comfy/vendor/redactor/i18n/az.js +75 -0
  103. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ba.js +79 -0
  104. data/app/assets/javascripts/comfy/vendor/redactor/i18n/bg.js +77 -0
  105. data/app/assets/javascripts/comfy/vendor/redactor/i18n/by.js +77 -0
  106. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ca.js +75 -0
  107. data/app/assets/javascripts/comfy/vendor/redactor/i18n/cs.js +87 -0
  108. data/app/assets/javascripts/comfy/vendor/redactor/i18n/da.js +75 -0
  109. data/app/assets/javascripts/comfy/vendor/redactor/i18n/de.js +80 -0
  110. data/app/assets/javascripts/comfy/vendor/redactor/i18n/el.js +76 -0
  111. data/app/assets/javascripts/comfy/vendor/redactor/i18n/en.js +75 -0
  112. data/app/assets/javascripts/comfy/vendor/redactor/i18n/eo.js +76 -0
  113. data/app/assets/javascripts/comfy/vendor/redactor/i18n/es.js +75 -0
  114. data/app/assets/javascripts/comfy/vendor/redactor/i18n/es_AR.js +76 -0
  115. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fa.js +76 -0
  116. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fi.js +75 -0
  117. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fr.js +75 -0
  118. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ge.js +75 -0
  119. data/app/assets/javascripts/comfy/vendor/redactor/i18n/gr.js +75 -0
  120. data/app/assets/javascripts/comfy/vendor/redactor/i18n/he.js +75 -0
  121. data/app/assets/javascripts/comfy/vendor/redactor/i18n/hr.js +76 -0
  122. data/app/assets/javascripts/comfy/vendor/redactor/i18n/hu.js +77 -0
  123. data/app/assets/javascripts/comfy/vendor/redactor/i18n/id.js +76 -0
  124. data/app/assets/javascripts/comfy/vendor/redactor/i18n/it.js +78 -0
  125. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ja.js +76 -0
  126. data/app/assets/javascripts/comfy/vendor/redactor/i18n/kn.js +75 -0
  127. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ko.js +76 -0
  128. data/app/assets/javascripts/comfy/vendor/redactor/i18n/lt.js +75 -0
  129. data/app/assets/javascripts/comfy/vendor/redactor/i18n/lv.js +75 -0
  130. data/app/assets/javascripts/comfy/vendor/redactor/i18n/mk.js +75 -0
  131. data/app/assets/javascripts/comfy/vendor/redactor/i18n/nb.js +75 -0
  132. data/app/assets/javascripts/comfy/vendor/redactor/i18n/nl.js +80 -0
  133. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pl.js +76 -0
  134. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pt-BR.js +75 -0
  135. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pt.js +75 -0
  136. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ro.js +76 -0
  137. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ru.js +76 -0
  138. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sk.js +76 -0
  139. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sl.js +79 -0
  140. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sq.js +79 -0
  141. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sr-CIR.js +79 -0
  142. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sr-LAT.js +79 -0
  143. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sv.js +74 -0
  144. data/app/assets/javascripts/comfy/vendor/redactor/i18n/th.js +74 -0
  145. data/app/assets/javascripts/comfy/vendor/redactor/i18n/tr.js +75 -0
  146. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ua.js +77 -0
  147. data/app/assets/javascripts/comfy/vendor/redactor/i18n/uk.js +77 -0
  148. data/app/assets/javascripts/comfy/vendor/redactor/i18n/vi.js +74 -0
  149. data/app/assets/javascripts/comfy/vendor/redactor/i18n/zh-CN.js +75 -0
  150. data/app/assets/javascripts/comfy/vendor/redactor/i18n/zh-TW.js +75 -0
  151. data/app/assets/javascripts/comfy/vendor/redactor/imagemanager.js +57 -0
  152. data/app/assets/javascripts/comfy/vendor/redactor/table.js +337 -0
  153. data/app/assets/javascripts/comfy/vendor/redactor/video.js +77 -0
  154. data/app/assets/javascripts/comfy/vendor/redactor.js +9561 -0
  155. data/app/assets/javascripts/comfy/vendor/sortable.min.js +5 -0
  156. data/app/assets/stylesheets/comfy/admin/cms/application.sass +11 -0
  157. data/app/assets/stylesheets/comfy/admin/cms/base.sass +256 -0
  158. data/app/assets/stylesheets/comfy/admin/cms/codemirror_overrides.sass +19 -0
  159. data/app/assets/stylesheets/comfy/admin/cms/custom.sass +1 -0
  160. data/app/assets/stylesheets/comfy/admin/cms/redactor_overrides.sass +22 -0
  161. data/app/assets/stylesheets/comfy/vendor/bootstrap/_alert.scss +51 -0
  162. data/app/assets/stylesheets/comfy/vendor/bootstrap/_badge.scss +54 -0
  163. data/app/assets/stylesheets/comfy/vendor/bootstrap/_breadcrumb.scss +41 -0
  164. data/app/assets/stylesheets/comfy/vendor/bootstrap/_button-group.scss +163 -0
  165. data/app/assets/stylesheets/comfy/vendor/bootstrap/_buttons.scss +137 -0
  166. data/app/assets/stylesheets/comfy/vendor/bootstrap/_card.scss +289 -0
  167. data/app/assets/stylesheets/comfy/vendor/bootstrap/_carousel.scss +197 -0
  168. data/app/assets/stylesheets/comfy/vendor/bootstrap/_close.scss +41 -0
  169. data/app/assets/stylesheets/comfy/vendor/bootstrap/_code.scss +48 -0
  170. data/app/assets/stylesheets/comfy/vendor/bootstrap/_custom-forms.scss +507 -0
  171. data/app/assets/stylesheets/comfy/vendor/bootstrap/_dropdown.scss +191 -0
  172. data/app/assets/stylesheets/comfy/vendor/bootstrap/_forms.scss +330 -0
  173. data/app/assets/stylesheets/comfy/vendor/bootstrap/_functions.scss +86 -0
  174. data/app/assets/stylesheets/comfy/vendor/bootstrap/_grid.scss +52 -0
  175. data/app/assets/stylesheets/comfy/vendor/bootstrap/_images.scss +42 -0
  176. data/app/assets/stylesheets/comfy/vendor/bootstrap/_input-group.scss +193 -0
  177. data/app/assets/stylesheets/comfy/vendor/bootstrap/_jumbotron.scss +17 -0
  178. data/app/assets/stylesheets/comfy/vendor/bootstrap/_list-group.scss +149 -0
  179. data/app/assets/stylesheets/comfy/vendor/bootstrap/_media.scss +8 -0
  180. data/app/assets/stylesheets/comfy/vendor/bootstrap/_mixins.scss +47 -0
  181. data/app/assets/stylesheets/comfy/vendor/bootstrap/_modal.scss +229 -0
  182. data/app/assets/stylesheets/comfy/vendor/bootstrap/_nav.scss +120 -0
  183. data/app/assets/stylesheets/comfy/vendor/bootstrap/_navbar.scss +294 -0
  184. data/app/assets/stylesheets/comfy/vendor/bootstrap/_pagination.scss +73 -0
  185. data/app/assets/stylesheets/comfy/vendor/bootstrap/_popover.scss +171 -0
  186. data/app/assets/stylesheets/comfy/vendor/bootstrap/_print.scss +141 -0
  187. data/app/assets/stylesheets/comfy/vendor/bootstrap/_progress.scss +43 -0
  188. data/app/assets/stylesheets/comfy/vendor/bootstrap/_reboot.scss +483 -0
  189. data/app/assets/stylesheets/comfy/vendor/bootstrap/_root.scss +19 -0
  190. data/app/assets/stylesheets/comfy/vendor/bootstrap/_spinners.scss +55 -0
  191. data/app/assets/stylesheets/comfy/vendor/bootstrap/_tables.scss +185 -0
  192. data/app/assets/stylesheets/comfy/vendor/bootstrap/_toasts.scss +44 -0
  193. data/app/assets/stylesheets/comfy/vendor/bootstrap/_tooltip.scss +115 -0
  194. data/app/assets/stylesheets/comfy/vendor/bootstrap/_transitions.scss +20 -0
  195. data/app/assets/stylesheets/comfy/vendor/bootstrap/_type.scss +125 -0
  196. data/app/assets/stylesheets/comfy/vendor/bootstrap/_utilities.scss +17 -0
  197. data/app/assets/stylesheets/comfy/vendor/bootstrap/_variables.scss +1123 -0
  198. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap-grid.scss +29 -0
  199. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap-reboot.scss +12 -0
  200. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap.scss +44 -0
  201. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_alert.scss +13 -0
  202. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_background-variant.scss +21 -0
  203. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_badge.scss +17 -0
  204. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_border-radius.scss +63 -0
  205. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_box-shadow.scss +20 -0
  206. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_breakpoints.scss +123 -0
  207. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_buttons.scss +107 -0
  208. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_caret.scss +62 -0
  209. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_clearfix.scss +7 -0
  210. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_deprecate.scss +10 -0
  211. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_float.scss +14 -0
  212. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_forms.scss +192 -0
  213. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_gradients.scss +45 -0
  214. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_grid-framework.scss +66 -0
  215. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_grid.scss +51 -0
  216. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_hover.scss +37 -0
  217. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_image.scss +36 -0
  218. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_list-group.scss +21 -0
  219. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_lists.scss +7 -0
  220. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_nav-divider.scss +10 -0
  221. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_pagination.scss +22 -0
  222. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_reset-text.scss +17 -0
  223. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_resize.scss +6 -0
  224. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_screen-reader.scss +33 -0
  225. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_size.scss +7 -0
  226. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_table-row.scss +39 -0
  227. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-emphasis.scss +16 -0
  228. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-hide.scss +11 -0
  229. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-truncate.scss +8 -0
  230. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_transition.scss +16 -0
  231. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_visibility.scss +8 -0
  232. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_align.scss +8 -0
  233. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_background.scss +19 -0
  234. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_borders.scss +75 -0
  235. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_clearfix.scss +3 -0
  236. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_display.scss +26 -0
  237. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_embed.scss +39 -0
  238. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_flex.scss +51 -0
  239. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_float.scss +11 -0
  240. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_overflow.scss +5 -0
  241. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_position.scss +32 -0
  242. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_screenreaders.scss +11 -0
  243. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_shadows.scss +6 -0
  244. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_sizing.scss +20 -0
  245. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_spacing.scss +73 -0
  246. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_stretched-link.scss +19 -0
  247. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_text.scss +72 -0
  248. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_visibility.scss +13 -0
  249. data/app/assets/stylesheets/comfy/vendor/bootstrap/vendor/_rfs.scss +204 -0
  250. data/app/assets/stylesheets/comfy/vendor/codemirror.css +346 -0
  251. data/app/assets/stylesheets/comfy/vendor/flatpickr.min.css +13 -0
  252. data/app/assets/stylesheets/comfy/vendor/fontawesome.css +343 -0
  253. data/app/assets/stylesheets/comfy/vendor/redactor.css +948 -0
  254. data/app/controllers/application_controller.rb +5 -0
  255. data/app/controllers/comfy/admin/base_controller.rb +20 -0
  256. data/app/controllers/comfy/admin/cms/base_controller.rb +54 -0
  257. data/app/controllers/comfy/admin/cms/categories_controller.rb +38 -0
  258. data/app/controllers/comfy/admin/cms/files_controller.rb +139 -0
  259. data/app/controllers/comfy/admin/cms/layouts_controller.rb +68 -0
  260. data/app/controllers/comfy/admin/cms/pages_controller.rb +163 -0
  261. data/app/controllers/comfy/admin/cms/revisions/base_controller.rb +48 -0
  262. data/app/controllers/comfy/admin/cms/revisions/layout_controller.rb +16 -0
  263. data/app/controllers/comfy/admin/cms/revisions/page_controller.rb +28 -0
  264. data/app/controllers/comfy/admin/cms/revisions/snippet_controller.rb +16 -0
  265. data/app/controllers/comfy/admin/cms/revisions/translation_controller.rb +29 -0
  266. data/app/controllers/comfy/admin/cms/sites_controller.rb +68 -0
  267. data/app/controllers/comfy/admin/cms/snippets_controller.rb +65 -0
  268. data/app/controllers/comfy/admin/cms/translations_controller.rb +105 -0
  269. data/app/controllers/comfy/cms/assets_controller.rb +38 -0
  270. data/app/controllers/comfy/cms/base_controller.rb +26 -0
  271. data/app/controllers/comfy/cms/content_controller.rb +90 -0
  272. data/app/controllers/concerns/comfy/paginate.rb +11 -0
  273. data/app/controllers/concerns/comfy/reorder_action.rb +20 -0
  274. data/app/helpers/comfy/admin/cms_helper.rb +50 -0
  275. data/app/helpers/comfy/cms_helper.rb +71 -0
  276. data/app/models/comfy/cms/categorization.rb +14 -0
  277. data/app/models/comfy/cms/category.rb +22 -0
  278. data/app/models/comfy/cms/file.rb +65 -0
  279. data/app/models/comfy/cms/fragment.rb +58 -0
  280. data/app/models/comfy/cms/layout.rb +114 -0
  281. data/app/models/comfy/cms/page.rb +173 -0
  282. data/app/models/comfy/cms/revision.rb +10 -0
  283. data/app/models/comfy/cms/site.rb +93 -0
  284. data/app/models/comfy/cms/snippet.rb +43 -0
  285. data/app/models/comfy/cms/translation.rb +42 -0
  286. data/app/models/concerns/comfy/cms/with_categories.rb +51 -0
  287. data/app/models/concerns/comfy/cms/with_fragments.rb +123 -0
  288. data/app/views/comfy/admin/cms/categories/_categories.html.haml +4 -0
  289. data/app/views/comfy/admin/cms/categories/_edit.html.haml +7 -0
  290. data/app/views/comfy/admin/cms/categories/_form.html.haml +5 -0
  291. data/app/views/comfy/admin/cms/categories/_index.html.haml +28 -0
  292. data/app/views/comfy/admin/cms/categories/_show.html.haml +15 -0
  293. data/app/views/comfy/admin/cms/categories/create.js.erb +7 -0
  294. data/app/views/comfy/admin/cms/categories/destroy.js.erb +8 -0
  295. data/app/views/comfy/admin/cms/categories/edit.js.erb +3 -0
  296. data/app/views/comfy/admin/cms/categories/update.js.erb +6 -0
  297. data/app/views/comfy/admin/cms/files/_file.html.haml +31 -0
  298. data/app/views/comfy/admin/cms/files/_form.html.haml +26 -0
  299. data/app/views/comfy/admin/cms/files/_modal.html.haml +3 -0
  300. data/app/views/comfy/admin/cms/files/destroy.js.erb +4 -0
  301. data/app/views/comfy/admin/cms/files/edit.html.haml +5 -0
  302. data/app/views/comfy/admin/cms/files/index.html.haml +31 -0
  303. data/app/views/comfy/admin/cms/files/new.html.haml +5 -0
  304. data/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml +23 -0
  305. data/app/views/comfy/admin/cms/fragments/_form_fragments.html.haml +38 -0
  306. data/app/views/comfy/admin/cms/layouts/_form.html.haml +20 -0
  307. data/app/views/comfy/admin/cms/layouts/_index_branch.html.haml +26 -0
  308. data/app/views/comfy/admin/cms/layouts/edit.html.haml +9 -0
  309. data/app/views/comfy/admin/cms/layouts/index.html.haml +10 -0
  310. data/app/views/comfy/admin/cms/layouts/new.html.haml +5 -0
  311. data/app/views/comfy/admin/cms/pages/_form.html.haml +31 -0
  312. data/app/views/comfy/admin/cms/pages/_index_branch.html.haml +44 -0
  313. data/app/views/comfy/admin/cms/pages/edit.html.haml +10 -0
  314. data/app/views/comfy/admin/cms/pages/index.html.haml +13 -0
  315. data/app/views/comfy/admin/cms/pages/new.html.haml +5 -0
  316. data/app/views/comfy/admin/cms/pages/toggle_branch.js.erb +13 -0
  317. data/app/views/comfy/admin/cms/revisions/_sidebar.html.haml +2 -0
  318. data/app/views/comfy/admin/cms/revisions/show.html.haml +36 -0
  319. data/app/views/comfy/admin/cms/sites/_form.html.haml +15 -0
  320. data/app/views/comfy/admin/cms/sites/edit.html.haml +5 -0
  321. data/app/views/comfy/admin/cms/sites/index.html.haml +28 -0
  322. data/app/views/comfy/admin/cms/sites/new.html.haml +5 -0
  323. data/app/views/comfy/admin/cms/snippets/_form.html.haml +19 -0
  324. data/app/views/comfy/admin/cms/snippets/edit.html.haml +9 -0
  325. data/app/views/comfy/admin/cms/snippets/index.html.haml +33 -0
  326. data/app/views/comfy/admin/cms/snippets/new.html.haml +5 -0
  327. data/app/views/comfy/admin/cms/translations/_form.html.haml +20 -0
  328. data/app/views/comfy/admin/cms/translations/_sidebar.html.haml +19 -0
  329. data/app/views/comfy/admin/cms/translations/edit.html.haml +10 -0
  330. data/app/views/comfy/admin/cms/translations/new.html.haml +5 -0
  331. data/app/views/kaminari/comfy/_first_page.html.haml +3 -0
  332. data/app/views/kaminari/comfy/_gap.html.haml +2 -0
  333. data/app/views/kaminari/comfy/_last_page.html.haml +3 -0
  334. data/app/views/kaminari/comfy/_next_page.html.haml +2 -0
  335. data/app/views/kaminari/comfy/_page.html.haml +2 -0
  336. data/app/views/kaminari/comfy/_paginator.html.haml +11 -0
  337. data/app/views/kaminari/comfy/_prev_page.html.haml +2 -0
  338. data/app/views/layouts/comfy/admin/cms/_body.html.haml +14 -0
  339. data/app/views/layouts/comfy/admin/cms/_flash.html.haml +4 -0
  340. data/app/views/layouts/comfy/admin/cms/_footer_js.html.haml +6 -0
  341. data/app/views/layouts/comfy/admin/cms/_head.html.haml +19 -0
  342. data/app/views/layouts/comfy/admin/cms/_left.html.haml +46 -0
  343. data/app/views/layouts/comfy/admin/cms/_right.html.haml +5 -0
  344. data/app/views/layouts/comfy/admin/cms.html.haml +4 -0
  345. data/bin/bundle +3 -0
  346. data/bin/rails +4 -0
  347. data/bin/rake +4 -0
  348. data/bin/setup +36 -0
  349. data/bin/update +31 -0
  350. data/bin/yarn +11 -0
  351. data/comfortable_media_surfer.gemspec +37 -0
  352. data/config/application.rb +59 -0
  353. data/config/boot.rb +5 -0
  354. data/config/cable.yml +10 -0
  355. data/config/cms_routes.rb +8 -0
  356. data/config/database.yml +40 -0
  357. data/config/environment.rb +7 -0
  358. data/config/environments/development.rb +80 -0
  359. data/config/environments/production.rb +99 -0
  360. data/config/environments/test.rb +74 -0
  361. data/config/initializers/assets.rb +14 -0
  362. data/config/initializers/comfortable_media_surfer.rb +122 -0
  363. data/config/initializers/content_security_policy.rb +27 -0
  364. data/config/initializers/filter_parameter_logging.rb +10 -0
  365. data/config/initializers/inflections.rb +18 -0
  366. data/config/initializers/json.rb +9 -0
  367. data/config/initializers/new_framework_defaults_7_0.rb +144 -0
  368. data/config/initializers/new_framework_defaults_7_1.rb +225 -0
  369. data/config/initializers/permissions_policy.rb +15 -0
  370. data/config/locales/ar.yml +262 -0
  371. data/config/locales/ca.yml +262 -0
  372. data/config/locales/cs.yml +262 -0
  373. data/config/locales/da.yml +262 -0
  374. data/config/locales/de.yml +262 -0
  375. data/config/locales/en.yml +262 -0
  376. data/config/locales/es.yml +262 -0
  377. data/config/locales/fi.yml +262 -0
  378. data/config/locales/fr.yml +262 -0
  379. data/config/locales/gr.yml +262 -0
  380. data/config/locales/hr.yml +262 -0
  381. data/config/locales/it.yml +262 -0
  382. data/config/locales/ja.yml +262 -0
  383. data/config/locales/nb.yml +262 -0
  384. data/config/locales/nl.yml +262 -0
  385. data/config/locales/pl.yml +262 -0
  386. data/config/locales/pt-BR.yml +262 -0
  387. data/config/locales/ru.yml +262 -0
  388. data/config/locales/sk.yml +262 -0
  389. data/config/locales/sv.yml +262 -0
  390. data/config/locales/tr.yml +262 -0
  391. data/config/locales/uk.yml +262 -0
  392. data/config/locales/zh-CN.yml +262 -0
  393. data/config/locales/zh-TW.yml +262 -0
  394. data/config/storage.yml +35 -0
  395. data/config.ru +6 -0
  396. data/db/cms_seeds/sample-site/files/_default.jpg.yml +5 -0
  397. data/db/cms_seeds/sample-site/files/default.jpg +0 -0
  398. data/db/cms_seeds/sample-site/layouts/default/content.html +16 -0
  399. data/db/cms_seeds/sample-site/layouts/default/nested/content.html +14 -0
  400. data/db/cms_seeds/sample-site/pages/index/child_a/content.html +7 -0
  401. data/db/cms_seeds/sample-site/pages/index/child_b/content.html +6 -0
  402. data/db/cms_seeds/sample-site/pages/index/cms logo.png +0 -0
  403. data/db/cms_seeds/sample-site/pages/index/content.es.html +6 -0
  404. data/db/cms_seeds/sample-site/pages/index/content.fr.html +6 -0
  405. data/db/cms_seeds/sample-site/pages/index/content.html +25 -0
  406. data/db/cms_seeds/sample-site/pages/index/footer.png +0 -0
  407. data/db/cms_seeds/sample-site/pages/index/header.png +0 -0
  408. data/db/cms_seeds/sample-site/snippets/default.html +8 -0
  409. data/db/migrate/00_create_active_storage_tables.active_storage.rb +50 -0
  410. data/db/migrate/01_create_cms.rb +143 -0
  411. data/gemfiles/6.1.gemfile +31 -0
  412. data/gemfiles/7.0.gemfile +31 -0
  413. data/gemfiles/7.1.gemfile +31 -0
  414. data/gemfiles/7.2.gemfile +31 -0
  415. data/lib/comfortable_media_surfer/access_control/admin_authentication.rb +22 -0
  416. data/lib/comfortable_media_surfer/access_control/admin_authorization.rb +10 -0
  417. data/lib/comfortable_media_surfer/access_control/public_authentication.rb +10 -0
  418. data/lib/comfortable_media_surfer/access_control/public_authorization.rb +10 -0
  419. data/lib/comfortable_media_surfer/configuration.rb +138 -0
  420. data/lib/comfortable_media_surfer/content/block.rb +12 -0
  421. data/lib/comfortable_media_surfer/content/params_parser.rb +149 -0
  422. data/lib/comfortable_media_surfer/content/renderer.rb +146 -0
  423. data/lib/comfortable_media_surfer/content/tag.rb +48 -0
  424. data/lib/comfortable_media_surfer/content/tags/asset.rb +57 -0
  425. data/lib/comfortable_media_surfer/content/tags/audio.rb +39 -0
  426. data/lib/comfortable_media_surfer/content/tags/breadcrumbs.rb +43 -0
  427. data/lib/comfortable_media_surfer/content/tags/checkbox.rb +27 -0
  428. data/lib/comfortable_media_surfer/content/tags/children.rb +52 -0
  429. data/lib/comfortable_media_surfer/content/tags/date.rb +19 -0
  430. data/lib/comfortable_media_surfer/content/tags/datetime.rb +42 -0
  431. data/lib/comfortable_media_surfer/content/tags/file.rb +70 -0
  432. data/lib/comfortable_media_surfer/content/tags/file_link.rb +58 -0
  433. data/lib/comfortable_media_surfer/content/tags/files.rb +41 -0
  434. data/lib/comfortable_media_surfer/content/tags/fragment.rb +64 -0
  435. data/lib/comfortable_media_surfer/content/tags/helper.rb +53 -0
  436. data/lib/comfortable_media_surfer/content/tags/image.rb +56 -0
  437. data/lib/comfortable_media_surfer/content/tags/markdown.rb +22 -0
  438. data/lib/comfortable_media_surfer/content/tags/mixins/file_content.rb +42 -0
  439. data/lib/comfortable_media_surfer/content/tags/number.rb +18 -0
  440. data/lib/comfortable_media_surfer/content/tags/page_file_link.rb +86 -0
  441. data/lib/comfortable_media_surfer/content/tags/partial.rb +47 -0
  442. data/lib/comfortable_media_surfer/content/tags/siblings.rb +66 -0
  443. data/lib/comfortable_media_surfer/content/tags/snippet.rb +36 -0
  444. data/lib/comfortable_media_surfer/content/tags/template.rb +42 -0
  445. data/lib/comfortable_media_surfer/content/tags/text.rb +18 -0
  446. data/lib/comfortable_media_surfer/content/tags/textarea.rb +19 -0
  447. data/lib/comfortable_media_surfer/content/tags/wysiwyg.rb +19 -0
  448. data/lib/comfortable_media_surfer/content/tags.rb +29 -0
  449. data/lib/comfortable_media_surfer/content.rb +11 -0
  450. data/lib/comfortable_media_surfer/engine.rb +29 -0
  451. data/lib/comfortable_media_surfer/error.rb +24 -0
  452. data/lib/comfortable_media_surfer/extensions/acts_as_tree.rb +114 -0
  453. data/lib/comfortable_media_surfer/extensions/has_revisions.rb +68 -0
  454. data/lib/comfortable_media_surfer/extensions.rb +8 -0
  455. data/lib/comfortable_media_surfer/form_builder.rb +43 -0
  456. data/lib/comfortable_media_surfer/render_methods.rb +106 -0
  457. data/lib/comfortable_media_surfer/routing.rb +79 -0
  458. data/lib/comfortable_media_surfer/seeds/file/exporter.rb +37 -0
  459. data/lib/comfortable_media_surfer/seeds/file/importer.rb +59 -0
  460. data/lib/comfortable_media_surfer/seeds/layout/exporter.rb +38 -0
  461. data/lib/comfortable_media_surfer/seeds/layout/importer.rb +60 -0
  462. data/lib/comfortable_media_surfer/seeds/page/exporter.rb +88 -0
  463. data/lib/comfortable_media_surfer/seeds/page/importer.rb +217 -0
  464. data/lib/comfortable_media_surfer/seeds/snippet/exporter.rb +32 -0
  465. data/lib/comfortable_media_surfer/seeds/snippet/importer.rb +47 -0
  466. data/lib/comfortable_media_surfer/seeds.rb +112 -0
  467. data/lib/comfortable_media_surfer/version.rb +5 -0
  468. data/lib/comfortable_media_surfer/view_hooks.rb +33 -0
  469. data/lib/comfortable_media_surfer.rb +56 -0
  470. data/lib/generators/comfy/cms/README +28 -0
  471. data/lib/generators/comfy/cms/assets_generator.rb +16 -0
  472. data/lib/generators/comfy/cms/cms_generator.rb +67 -0
  473. data/lib/generators/comfy/cms/controllers_generator.rb +15 -0
  474. data/lib/generators/comfy/cms/models_generator.rb +15 -0
  475. data/lib/generators/comfy/cms/views_generator.rb +15 -0
  476. data/lib/generators/comfy/scaffold/scaffold_generator.rb +79 -0
  477. data/lib/generators/comfy/scaffold/templates/controller.rb.tt +62 -0
  478. data/lib/generators/comfy/scaffold/templates/migration.rb.tt +11 -0
  479. data/lib/generators/comfy/scaffold/templates/model.rb.tt +21 -0
  480. data/lib/generators/comfy/scaffold/templates/tests/controller.rb.tt +115 -0
  481. data/lib/generators/comfy/scaffold/templates/tests/fixture.yml.tt +4 -0
  482. data/lib/generators/comfy/scaffold/templates/tests/model.rb.tt +26 -0
  483. data/lib/generators/comfy/scaffold/templates/views/_form.haml.tt +7 -0
  484. data/lib/generators/comfy/scaffold/templates/views/edit.haml.tt +5 -0
  485. data/lib/generators/comfy/scaffold/templates/views/index.haml.tt +21 -0
  486. data/lib/generators/comfy/scaffold/templates/views/new.haml.tt +5 -0
  487. data/lib/generators/comfy/scaffold/templates/views/show.haml.tt +4 -0
  488. data/lib/tasks/cms_seeds.rake +40 -0
  489. data/public/favicon.ico +0 -0
  490. data/rakelib/create_release.rake +81 -0
  491. metadata +797 -0
@@ -0,0 +1,2497 @@
1
+ /**
2
+ * Plupload - multi-runtime File Uploader
3
+ * v2.3.6
4
+ *
5
+ * Copyright 2013, Moxiecode Systems AB
6
+ * Released under GPL License.
7
+ *
8
+ * License: http://www.plupload.com/license
9
+ * Contributing: http://www.plupload.com/contributing
10
+ *
11
+ * Date: 2017-11-03
12
+ */
13
+ ;(function (global, factory) {
14
+ var extract = function() {
15
+ var ctx = {};
16
+ factory.apply(ctx, arguments);
17
+ return ctx.plupload;
18
+ };
19
+
20
+ if (typeof define === "function" && define.amd) {
21
+ define("plupload", ['./moxie'], extract);
22
+ } else if (typeof module === "object" && module.exports) {
23
+ module.exports = extract(require('./moxie'));
24
+ } else {
25
+ global.plupload = extract(global.moxie);
26
+ }
27
+ }(this || window, function(moxie) {
28
+ /**
29
+ * Plupload.js
30
+ *
31
+ * Copyright 2013, Moxiecode Systems AB
32
+ * Released under GPL License.
33
+ *
34
+ * License: http://www.plupload.com/license
35
+ * Contributing: http://www.plupload.com/contributing
36
+ */
37
+
38
+ ;(function(exports, o, undef) {
39
+
40
+ var delay = window.setTimeout;
41
+ var fileFilters = {};
42
+ var u = o.core.utils;
43
+ var Runtime = o.runtime.Runtime;
44
+
45
+ // convert plupload features to caps acceptable by mOxie
46
+ function normalizeCaps(settings) {
47
+ var features = settings.required_features, caps = {};
48
+
49
+ function resolve(feature, value, strict) {
50
+ // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
51
+ var map = {
52
+ chunks: 'slice_blob',
53
+ jpgresize: 'send_binary_string',
54
+ pngresize: 'send_binary_string',
55
+ progress: 'report_upload_progress',
56
+ multi_selection: 'select_multiple',
57
+ dragdrop: 'drag_and_drop',
58
+ drop_element: 'drag_and_drop',
59
+ headers: 'send_custom_headers',
60
+ urlstream_upload: 'send_binary_string',
61
+ canSendBinary: 'send_binary',
62
+ triggerDialog: 'summon_file_dialog'
63
+ };
64
+
65
+ if (map[feature]) {
66
+ caps[map[feature]] = value;
67
+ } else if (!strict) {
68
+ caps[feature] = value;
69
+ }
70
+ }
71
+
72
+ if (typeof(features) === 'string') {
73
+ plupload.each(features.split(/\s*,\s*/), function(feature) {
74
+ resolve(feature, true);
75
+ });
76
+ } else if (typeof(features) === 'object') {
77
+ plupload.each(features, function(value, feature) {
78
+ resolve(feature, value);
79
+ });
80
+ } else if (features === true) {
81
+ // check settings for required features
82
+ if (settings.chunk_size && settings.chunk_size > 0) {
83
+ caps.slice_blob = true;
84
+ }
85
+
86
+ if (!plupload.isEmptyObj(settings.resize) || settings.multipart === false) {
87
+ caps.send_binary_string = true;
88
+ }
89
+
90
+ if (settings.http_method) {
91
+ caps.use_http_method = settings.http_method;
92
+ }
93
+
94
+ plupload.each(settings, function(value, feature) {
95
+ resolve(feature, !!value, true); // strict check
96
+ });
97
+ }
98
+
99
+ return caps;
100
+ }
101
+
102
+ /**
103
+ * @module plupload
104
+ * @static
105
+ */
106
+ var plupload = {
107
+ /**
108
+ * Plupload version will be replaced on build.
109
+ *
110
+ * @property VERSION
111
+ * @for Plupload
112
+ * @static
113
+ * @final
114
+ */
115
+ VERSION : '2.3.6',
116
+
117
+ /**
118
+ * The state of the queue before it has started and after it has finished
119
+ *
120
+ * @property STOPPED
121
+ * @static
122
+ * @final
123
+ */
124
+ STOPPED : 1,
125
+
126
+ /**
127
+ * Upload process is running
128
+ *
129
+ * @property STARTED
130
+ * @static
131
+ * @final
132
+ */
133
+ STARTED : 2,
134
+
135
+ /**
136
+ * File is queued for upload
137
+ *
138
+ * @property QUEUED
139
+ * @static
140
+ * @final
141
+ */
142
+ QUEUED : 1,
143
+
144
+ /**
145
+ * File is being uploaded
146
+ *
147
+ * @property UPLOADING
148
+ * @static
149
+ * @final
150
+ */
151
+ UPLOADING : 2,
152
+
153
+ /**
154
+ * File has failed to be uploaded
155
+ *
156
+ * @property FAILED
157
+ * @static
158
+ * @final
159
+ */
160
+ FAILED : 4,
161
+
162
+ /**
163
+ * File has been uploaded successfully
164
+ *
165
+ * @property DONE
166
+ * @static
167
+ * @final
168
+ */
169
+ DONE : 5,
170
+
171
+ // Error constants used by the Error event
172
+
173
+ /**
174
+ * Generic error for example if an exception is thrown inside Silverlight.
175
+ *
176
+ * @property GENERIC_ERROR
177
+ * @static
178
+ * @final
179
+ */
180
+ GENERIC_ERROR : -100,
181
+
182
+ /**
183
+ * HTTP transport error. For example if the server produces a HTTP status other than 200.
184
+ *
185
+ * @property HTTP_ERROR
186
+ * @static
187
+ * @final
188
+ */
189
+ HTTP_ERROR : -200,
190
+
191
+ /**
192
+ * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
193
+ *
194
+ * @property IO_ERROR
195
+ * @static
196
+ * @final
197
+ */
198
+ IO_ERROR : -300,
199
+
200
+ /**
201
+ * @property SECURITY_ERROR
202
+ * @static
203
+ * @final
204
+ */
205
+ SECURITY_ERROR : -400,
206
+
207
+ /**
208
+ * Initialization error. Will be triggered if no runtime was initialized.
209
+ *
210
+ * @property INIT_ERROR
211
+ * @static
212
+ * @final
213
+ */
214
+ INIT_ERROR : -500,
215
+
216
+ /**
217
+ * File size error. If the user selects a file that is too large or is empty it will be blocked and
218
+ * an error of this type will be triggered.
219
+ *
220
+ * @property FILE_SIZE_ERROR
221
+ * @static
222
+ * @final
223
+ */
224
+ FILE_SIZE_ERROR : -600,
225
+
226
+ /**
227
+ * File extension error. If the user selects a file that isn't valid according to the filters setting.
228
+ *
229
+ * @property FILE_EXTENSION_ERROR
230
+ * @static
231
+ * @final
232
+ */
233
+ FILE_EXTENSION_ERROR : -601,
234
+
235
+ /**
236
+ * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
237
+ *
238
+ * @property FILE_DUPLICATE_ERROR
239
+ * @static
240
+ * @final
241
+ */
242
+ FILE_DUPLICATE_ERROR : -602,
243
+
244
+ /**
245
+ * Runtime will try to detect if image is proper one. Otherwise will throw this error.
246
+ *
247
+ * @property IMAGE_FORMAT_ERROR
248
+ * @static
249
+ * @final
250
+ */
251
+ IMAGE_FORMAT_ERROR : -700,
252
+
253
+ /**
254
+ * While working on files runtime may run out of memory and will throw this error.
255
+ *
256
+ * @since 2.1.2
257
+ * @property MEMORY_ERROR
258
+ * @static
259
+ * @final
260
+ */
261
+ MEMORY_ERROR : -701,
262
+
263
+ /**
264
+ * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
265
+ *
266
+ * @property IMAGE_DIMENSIONS_ERROR
267
+ * @static
268
+ * @final
269
+ */
270
+ IMAGE_DIMENSIONS_ERROR : -702,
271
+
272
+ /**
273
+ * Expose whole moxie (#1469).
274
+ *
275
+ * @property moxie
276
+ * @type Object
277
+ * @final
278
+ */
279
+ moxie: o,
280
+
281
+ /**
282
+ * Mime type lookup table.
283
+ *
284
+ * @property mimeTypes
285
+ * @type Object
286
+ * @final
287
+ */
288
+ mimeTypes : u.Mime.mimes,
289
+
290
+ /**
291
+ * In some cases sniffing is the only way around :(
292
+ */
293
+ ua: u.Env,
294
+
295
+ /**
296
+ * Gets the true type of the built-in object (better version of typeof).
297
+ * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
298
+ *
299
+ * @method typeOf
300
+ * @static
301
+ * @param {Object} o Object to check.
302
+ * @return {String} Object [[Class]]
303
+ */
304
+ typeOf: u.Basic.typeOf,
305
+
306
+ /**
307
+ * Extends the specified object with another object.
308
+ *
309
+ * @method extend
310
+ * @static
311
+ * @param {Object} target Object to extend.
312
+ * @param {Object..} obj Multiple objects to extend with.
313
+ * @return {Object} Same as target, the extended object.
314
+ */
315
+ extend : u.Basic.extend,
316
+
317
+ /**
318
+ * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
319
+ * The only way a user would be able to get the same ID is if the two persons at the same exact millisecond manages
320
+ * to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
321
+ * It's more probable for the earth to be hit with an asteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property
322
+ * to an user unique key.
323
+ *
324
+ * @method guid
325
+ * @static
326
+ * @return {String} Virtually unique id.
327
+ */
328
+ guid : u.Basic.guid,
329
+
330
+ /**
331
+ * Get array of DOM Elements by their ids.
332
+ *
333
+ * @method get
334
+ * @param {String} id Identifier of the DOM Element
335
+ * @return {Array}
336
+ */
337
+ getAll : function get(ids) {
338
+ var els = [], el;
339
+
340
+ if (plupload.typeOf(ids) !== 'array') {
341
+ ids = [ids];
342
+ }
343
+
344
+ var i = ids.length;
345
+ while (i--) {
346
+ el = plupload.get(ids[i]);
347
+ if (el) {
348
+ els.push(el);
349
+ }
350
+ }
351
+
352
+ return els.length ? els : null;
353
+ },
354
+
355
+ /**
356
+ Get DOM element by id
357
+
358
+ @method get
359
+ @param {String} id Identifier of the DOM Element
360
+ @return {Node}
361
+ */
362
+ get: u.Dom.get,
363
+
364
+ /**
365
+ * Executes the callback function for each item in array/object. If you return false in the
366
+ * callback it will break the loop.
367
+ *
368
+ * @method each
369
+ * @static
370
+ * @param {Object} obj Object to iterate.
371
+ * @param {function} callback Callback function to execute for each item.
372
+ */
373
+ each : u.Basic.each,
374
+
375
+ /**
376
+ * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
377
+ *
378
+ * @method getPos
379
+ * @static
380
+ * @param {Element} node HTML element or element id to get x, y position from.
381
+ * @param {Element} root Optional root element to stop calculations at.
382
+ * @return {object} Absolute position of the specified element object with x, y fields.
383
+ */
384
+ getPos : u.Dom.getPos,
385
+
386
+ /**
387
+ * Returns the size of the specified node in pixels.
388
+ *
389
+ * @method getSize
390
+ * @static
391
+ * @param {Node} node Node to get the size of.
392
+ * @return {Object} Object with a w and h property.
393
+ */
394
+ getSize : u.Dom.getSize,
395
+
396
+ /**
397
+ * Encodes the specified string.
398
+ *
399
+ * @method xmlEncode
400
+ * @static
401
+ * @param {String} s String to encode.
402
+ * @return {String} Encoded string.
403
+ */
404
+ xmlEncode : function(str) {
405
+ var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
406
+
407
+ return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
408
+ return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
409
+ }) : str;
410
+ },
411
+
412
+ /**
413
+ * Forces anything into an array.
414
+ *
415
+ * @method toArray
416
+ * @static
417
+ * @param {Object} obj Object with length field.
418
+ * @return {Array} Array object containing all items.
419
+ */
420
+ toArray : u.Basic.toArray,
421
+
422
+ /**
423
+ * Find an element in array and return its index if present, otherwise return -1.
424
+ *
425
+ * @method inArray
426
+ * @static
427
+ * @param {mixed} needle Element to find
428
+ * @param {Array} array
429
+ * @return {Int} Index of the element, or -1 if not found
430
+ */
431
+ inArray : u.Basic.inArray,
432
+
433
+ /**
434
+ Recieve an array of functions (usually async) to call in sequence, each function
435
+ receives a callback as first argument that it should call, when it completes. Finally,
436
+ after everything is complete, main callback is called. Passing truthy value to the
437
+ callback as a first argument will interrupt the sequence and invoke main callback
438
+ immediately.
439
+
440
+ @method inSeries
441
+ @static
442
+ @param {Array} queue Array of functions to call in sequence
443
+ @param {Function} cb Main callback that is called in the end, or in case of error
444
+ */
445
+ inSeries: u.Basic.inSeries,
446
+
447
+ /**
448
+ * Extends the language pack object with new items.
449
+ *
450
+ * @method addI18n
451
+ * @static
452
+ * @param {Object} pack Language pack items to add.
453
+ * @return {Object} Extended language pack object.
454
+ */
455
+ addI18n : o.core.I18n.addI18n,
456
+
457
+ /**
458
+ * Translates the specified string by checking for the english string in the language pack lookup.
459
+ *
460
+ * @method translate
461
+ * @static
462
+ * @param {String} str String to look for.
463
+ * @return {String} Translated string or the input string if it wasn't found.
464
+ */
465
+ translate : o.core.I18n.translate,
466
+
467
+ /**
468
+ * Pseudo sprintf implementation - simple way to replace tokens with specified values.
469
+ *
470
+ * @param {String} str String with tokens
471
+ * @return {String} String with replaced tokens
472
+ */
473
+ sprintf : u.Basic.sprintf,
474
+
475
+ /**
476
+ * Checks if object is empty.
477
+ *
478
+ * @method isEmptyObj
479
+ * @static
480
+ * @param {Object} obj Object to check.
481
+ * @return {Boolean}
482
+ */
483
+ isEmptyObj : u.Basic.isEmptyObj,
484
+
485
+ /**
486
+ * Checks if specified DOM element has specified class.
487
+ *
488
+ * @method hasClass
489
+ * @static
490
+ * @param {Object} obj DOM element like object to add handler to.
491
+ * @param {String} name Class name
492
+ */
493
+ hasClass : u.Dom.hasClass,
494
+
495
+ /**
496
+ * Adds specified className to specified DOM element.
497
+ *
498
+ * @method addClass
499
+ * @static
500
+ * @param {Object} obj DOM element like object to add handler to.
501
+ * @param {String} name Class name
502
+ */
503
+ addClass : u.Dom.addClass,
504
+
505
+ /**
506
+ * Removes specified className from specified DOM element.
507
+ *
508
+ * @method removeClass
509
+ * @static
510
+ * @param {Object} obj DOM element like object to add handler to.
511
+ * @param {String} name Class name
512
+ */
513
+ removeClass : u.Dom.removeClass,
514
+
515
+ /**
516
+ * Returns a given computed style of a DOM element.
517
+ *
518
+ * @method getStyle
519
+ * @static
520
+ * @param {Object} obj DOM element like object.
521
+ * @param {String} name Style you want to get from the DOM element
522
+ */
523
+ getStyle : u.Dom.getStyle,
524
+
525
+ /**
526
+ * Adds an event handler to the specified object and store reference to the handler
527
+ * in objects internal Plupload registry (@see removeEvent).
528
+ *
529
+ * @method addEvent
530
+ * @static
531
+ * @param {Object} obj DOM element like object to add handler to.
532
+ * @param {String} name Name to add event listener to.
533
+ * @param {Function} callback Function to call when event occurs.
534
+ * @param {String} (optional) key that might be used to add specifity to the event record.
535
+ */
536
+ addEvent : u.Events.addEvent,
537
+
538
+ /**
539
+ * Remove event handler from the specified object. If third argument (callback)
540
+ * is not specified remove all events with the specified name.
541
+ *
542
+ * @method removeEvent
543
+ * @static
544
+ * @param {Object} obj DOM element to remove event listener(s) from.
545
+ * @param {String} name Name of event listener to remove.
546
+ * @param {Function|String} (optional) might be a callback or unique key to match.
547
+ */
548
+ removeEvent: u.Events.removeEvent,
549
+
550
+ /**
551
+ * Remove all kind of events from the specified object
552
+ *
553
+ * @method removeAllEvents
554
+ * @static
555
+ * @param {Object} obj DOM element to remove event listeners from.
556
+ * @param {String} (optional) unique key to match, when removing events.
557
+ */
558
+ removeAllEvents: u.Events.removeAllEvents,
559
+
560
+ /**
561
+ * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
562
+ *
563
+ * @method cleanName
564
+ * @static
565
+ * @param {String} s String to clean up.
566
+ * @return {String} Cleaned string.
567
+ */
568
+ cleanName : function(name) {
569
+ var i, lookup;
570
+
571
+ // Replace diacritics
572
+ lookup = [
573
+ /[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
574
+ /\307/g, 'C', /\347/g, 'c',
575
+ /[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
576
+ /[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
577
+ /\321/g, 'N', /\361/g, 'n',
578
+ /[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
579
+ /[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
580
+ ];
581
+
582
+ for (i = 0; i < lookup.length; i += 2) {
583
+ name = name.replace(lookup[i], lookup[i + 1]);
584
+ }
585
+
586
+ // Replace whitespace
587
+ name = name.replace(/\s+/g, '_');
588
+
589
+ // Remove anything else
590
+ name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
591
+
592
+ return name;
593
+ },
594
+
595
+ /**
596
+ * Builds a full url out of a base URL and an object with items to append as query string items.
597
+ *
598
+ * @method buildUrl
599
+ * @static
600
+ * @param {String} url Base URL to append query string items to.
601
+ * @param {Object} items Name/value object to serialize as a querystring.
602
+ * @return {String} String with url + serialized query string items.
603
+ */
604
+ buildUrl: function(url, items) {
605
+ var query = '';
606
+
607
+ plupload.each(items, function(value, name) {
608
+ query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
609
+ });
610
+
611
+ if (query) {
612
+ url += (url.indexOf('?') > 0 ? '&' : '?') + query;
613
+ }
614
+
615
+ return url;
616
+ },
617
+
618
+ /**
619
+ * Formats the specified number as a size string for example 1024 becomes 1 KB.
620
+ *
621
+ * @method formatSize
622
+ * @static
623
+ * @param {Number} size Size to format as string.
624
+ * @return {String} Formatted size string.
625
+ */
626
+ formatSize : function(size) {
627
+
628
+ if (size === undef || /\D/.test(size)) {
629
+ return plupload.translate('N/A');
630
+ }
631
+
632
+ function round(num, precision) {
633
+ return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
634
+ }
635
+
636
+ var boundary = Math.pow(1024, 4);
637
+
638
+ // TB
639
+ if (size > boundary) {
640
+ return round(size / boundary, 1) + " " + plupload.translate('tb');
641
+ }
642
+
643
+ // GB
644
+ if (size > (boundary/=1024)) {
645
+ return round(size / boundary, 1) + " " + plupload.translate('gb');
646
+ }
647
+
648
+ // MB
649
+ if (size > (boundary/=1024)) {
650
+ return round(size / boundary, 1) + " " + plupload.translate('mb');
651
+ }
652
+
653
+ // KB
654
+ if (size > 1024) {
655
+ return Math.round(size / 1024) + " " + plupload.translate('kb');
656
+ }
657
+
658
+ return size + " " + plupload.translate('b');
659
+ },
660
+
661
+
662
+ /**
663
+ * Parses the specified size string into a byte value. For example 10kb becomes 10240.
664
+ *
665
+ * @method parseSize
666
+ * @static
667
+ * @param {String|Number} size String to parse or number to just pass through.
668
+ * @return {Number} Size in bytes.
669
+ */
670
+ parseSize : u.Basic.parseSizeStr,
671
+
672
+
673
+ /**
674
+ * A way to predict what runtime will be choosen in the current environment with the
675
+ * specified settings.
676
+ *
677
+ * @method predictRuntime
678
+ * @static
679
+ * @param {Object|String} config Plupload settings to check
680
+ * @param {String} [runtimes] Comma-separated list of runtimes to check against
681
+ * @return {String} Type of compatible runtime
682
+ */
683
+ predictRuntime : function(config, runtimes) {
684
+ var up, runtime;
685
+
686
+ up = new plupload.Uploader(config);
687
+ runtime = Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
688
+ up.destroy();
689
+ return runtime;
690
+ },
691
+
692
+ /**
693
+ * Registers a filter that will be executed for each file added to the queue.
694
+ * If callback returns false, file will not be added.
695
+ *
696
+ * Callback receives two arguments: a value for the filter as it was specified in settings.filters
697
+ * and a file to be filtered. Callback is executed in the context of uploader instance.
698
+ *
699
+ * @method addFileFilter
700
+ * @static
701
+ * @param {String} name Name of the filter by which it can be referenced in settings.filters
702
+ * @param {String} cb Callback - the actual routine that every added file must pass
703
+ */
704
+ addFileFilter: function(name, cb) {
705
+ fileFilters[name] = cb;
706
+ }
707
+ };
708
+
709
+
710
+ plupload.addFileFilter('mime_types', function(filters, file, cb) {
711
+ if (filters.length && !filters.regexp.test(file.name)) {
712
+ this.trigger('Error', {
713
+ code : plupload.FILE_EXTENSION_ERROR,
714
+ message : plupload.translate('File extension error.'),
715
+ file : file
716
+ });
717
+ cb(false);
718
+ } else {
719
+ cb(true);
720
+ }
721
+ });
722
+
723
+
724
+ plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
725
+ var undef;
726
+
727
+ maxSize = plupload.parseSize(maxSize);
728
+
729
+ // Invalid file size
730
+ if (file.size !== undef && maxSize && file.size > maxSize) {
731
+ this.trigger('Error', {
732
+ code : plupload.FILE_SIZE_ERROR,
733
+ message : plupload.translate('File size error.'),
734
+ file : file
735
+ });
736
+ cb(false);
737
+ } else {
738
+ cb(true);
739
+ }
740
+ });
741
+
742
+
743
+ plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
744
+ if (value) {
745
+ var ii = this.files.length;
746
+ while (ii--) {
747
+ // Compare by name and size (size might be 0 or undefined, but still equivalent for both)
748
+ if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
749
+ this.trigger('Error', {
750
+ code : plupload.FILE_DUPLICATE_ERROR,
751
+ message : plupload.translate('Duplicate file error.'),
752
+ file : file
753
+ });
754
+ cb(false);
755
+ return;
756
+ }
757
+ }
758
+ }
759
+ cb(true);
760
+ });
761
+
762
+ plupload.addFileFilter('prevent_empty', function(value, file, cb) {
763
+ if (value && !file.size && file.size !== undef) {
764
+ this.trigger('Error', {
765
+ code : plupload.FILE_SIZE_ERROR,
766
+ message : plupload.translate('File size error.'),
767
+ file : file
768
+ });
769
+ cb(false);
770
+ } else {
771
+ cb(true);
772
+ }
773
+ });
774
+
775
+
776
+ /**
777
+ @class Uploader
778
+ @constructor
779
+
780
+ @param {Object} settings For detailed information about each option check documentation.
781
+ @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
782
+ @param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
783
+ @param {String|DOMElement} [settings.container] id of the DOM element or DOM element itself that will be used to wrap uploader structures. Defaults to immediate parent of the `browse_button` element.
784
+ @param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop.
785
+ @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
786
+ @param {Object} [settings.filters={}] Set of file type filters.
787
+ @param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
788
+ @param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
789
+ @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
790
+ @param {Boolean} [settings.filters.prevent_empty=true] Do not let empty files into the queue (IE10 is known to hang for example when trying to upload such). Dispatches `plupload.FILE_SIZE_ERROR`.
791
+ @param {String} [settings.flash_swf_url] URL of the Flash swf.
792
+ @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
793
+ @param {String} [settings.http_method="POST"] HTTP method to use during upload (only PUT or POST allowed).
794
+ @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
795
+ @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
796
+ @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
797
+ @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
798
+ @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
799
+ @param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
800
+ @param {Number} [settings.resize.width] If image is bigger, it will be resized.
801
+ @param {Number} [settings.resize.height] If image is bigger, it will be resized.
802
+ @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
803
+ @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
804
+ @param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
805
+ @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
806
+ @param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
807
+ @param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways).
808
+ @param {String} settings.url URL of the server-side upload handler.
809
+ @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
810
+
811
+ */
812
+ plupload.Uploader = function(options) {
813
+ /**
814
+ Fires when the current RunTime has been initialized.
815
+
816
+ @event Init
817
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
818
+ */
819
+
820
+ /**
821
+ Fires after the init event incase you need to perform actions there.
822
+
823
+ @event PostInit
824
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
825
+ */
826
+
827
+ /**
828
+ Fires when the option is changed in via uploader.setOption().
829
+
830
+ @event OptionChanged
831
+ @since 2.1
832
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
833
+ @param {String} name Name of the option that was changed
834
+ @param {Mixed} value New value for the specified option
835
+ @param {Mixed} oldValue Previous value of the option
836
+ */
837
+
838
+ /**
839
+ Fires when the silverlight/flash or other shim needs to move.
840
+
841
+ @event Refresh
842
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
843
+ */
844
+
845
+ /**
846
+ Fires when the overall state is being changed for the upload queue.
847
+
848
+ @event StateChanged
849
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
850
+ */
851
+
852
+ /**
853
+ Fires when browse_button is clicked and browse dialog shows.
854
+
855
+ @event Browse
856
+ @since 2.1.2
857
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
858
+ */
859
+
860
+ /**
861
+ Fires for every filtered file before it is added to the queue.
862
+
863
+ @event FileFiltered
864
+ @since 2.1
865
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
866
+ @param {plupload.File} file Another file that has to be added to the queue.
867
+ */
868
+
869
+ /**
870
+ Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
871
+
872
+ @event QueueChanged
873
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
874
+ */
875
+
876
+ /**
877
+ Fires after files were filtered and added to the queue.
878
+
879
+ @event FilesAdded
880
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
881
+ @param {Array} files Array of file objects that were added to queue by the user.
882
+ */
883
+
884
+ /**
885
+ Fires when file is removed from the queue.
886
+
887
+ @event FilesRemoved
888
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
889
+ @param {Array} files Array of files that got removed.
890
+ */
891
+
892
+ /**
893
+ Fires just before a file is uploaded. Can be used to cancel the upload for the specified file
894
+ by returning false from the handler.
895
+
896
+ @event BeforeUpload
897
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
898
+ @param {plupload.File} file File to be uploaded.
899
+ */
900
+
901
+ /**
902
+ Fires when a file is to be uploaded by the runtime.
903
+
904
+ @event UploadFile
905
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
906
+ @param {plupload.File} file File to be uploaded.
907
+ */
908
+
909
+ /**
910
+ Fires while a file is being uploaded. Use this event to update the current file upload progress.
911
+
912
+ @event UploadProgress
913
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
914
+ @param {plupload.File} file File that is currently being uploaded.
915
+ */
916
+
917
+ /**
918
+ * Fires just before a chunk is uploaded. This event enables you to override settings
919
+ * on the uploader instance before the chunk is uploaded.
920
+ *
921
+ * @event BeforeChunkUpload
922
+ * @param {plupload.Uploader} uploader Uploader instance sending the event.
923
+ * @param {plupload.File} file File to be uploaded.
924
+ * @param {Object} args POST params to be sent.
925
+ * @param {Blob} chunkBlob Current blob.
926
+ * @param {offset} offset Current offset.
927
+ */
928
+
929
+ /**
930
+ Fires when file chunk is uploaded.
931
+
932
+ @event ChunkUploaded
933
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
934
+ @param {plupload.File} file File that the chunk was uploaded for.
935
+ @param {Object} result Object with response properties.
936
+ @param {Number} result.offset The amount of bytes the server has received so far, including this chunk.
937
+ @param {Number} result.total The size of the file.
938
+ @param {String} result.response The response body sent by the server.
939
+ @param {Number} result.status The HTTP status code sent by the server.
940
+ @param {String} result.responseHeaders All the response headers as a single string.
941
+ */
942
+
943
+ /**
944
+ Fires when a file is successfully uploaded.
945
+
946
+ @event FileUploaded
947
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
948
+ @param {plupload.File} file File that was uploaded.
949
+ @param {Object} result Object with response properties.
950
+ @param {String} result.response The response body sent by the server.
951
+ @param {Number} result.status The HTTP status code sent by the server.
952
+ @param {String} result.responseHeaders All the response headers as a single string.
953
+ */
954
+
955
+ /**
956
+ Fires when all files in a queue are uploaded.
957
+
958
+ @event UploadComplete
959
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
960
+ @param {Array} files Array of file objects that was added to queue/selected by the user.
961
+ */
962
+
963
+ /**
964
+ Fires when a error occurs.
965
+
966
+ @event Error
967
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
968
+ @param {Object} error Contains code, message and sometimes file and other details.
969
+ @param {Number} error.code The plupload error code.
970
+ @param {String} error.message Description of the error (uses i18n).
971
+ */
972
+
973
+ /**
974
+ Fires when destroy method is called.
975
+
976
+ @event Destroy
977
+ @param {plupload.Uploader} uploader Uploader instance sending the event.
978
+ */
979
+ var uid = plupload.guid()
980
+ , settings
981
+ , files = []
982
+ , preferred_caps = {}
983
+ , fileInputs = []
984
+ , fileDrops = []
985
+ , startTime
986
+ , total
987
+ , disabled = false
988
+ , xhr
989
+ ;
990
+
991
+
992
+ // Private methods
993
+ function uploadNext() {
994
+ var file, count = 0, i;
995
+
996
+ if (this.state == plupload.STARTED) {
997
+ // Find first QUEUED file
998
+ for (i = 0; i < files.length; i++) {
999
+ if (!file && files[i].status == plupload.QUEUED) {
1000
+ file = files[i];
1001
+ if (this.trigger("BeforeUpload", file)) {
1002
+ file.status = plupload.UPLOADING;
1003
+ this.trigger("UploadFile", file);
1004
+ }
1005
+ } else {
1006
+ count++;
1007
+ }
1008
+ }
1009
+
1010
+ // All files are DONE or FAILED
1011
+ if (count == files.length) {
1012
+ if (this.state !== plupload.STOPPED) {
1013
+ this.state = plupload.STOPPED;
1014
+ this.trigger("StateChanged");
1015
+ }
1016
+ this.trigger("UploadComplete", files);
1017
+ }
1018
+ }
1019
+ }
1020
+
1021
+
1022
+ function calcFile(file) {
1023
+ file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
1024
+ calc();
1025
+ }
1026
+
1027
+
1028
+ function calc() {
1029
+ var i, file;
1030
+ var loaded;
1031
+ var loadedDuringCurrentSession = 0;
1032
+
1033
+ // Reset stats
1034
+ total.reset();
1035
+
1036
+ // Check status, size, loaded etc on all files
1037
+ for (i = 0; i < files.length; i++) {
1038
+ file = files[i];
1039
+
1040
+ if (file.size !== undef) {
1041
+ // We calculate totals based on original file size
1042
+ total.size += file.origSize;
1043
+
1044
+ // Since we cannot predict file size after resize, we do opposite and
1045
+ // interpolate loaded amount to match magnitude of total
1046
+ loaded = file.loaded * file.origSize / file.size;
1047
+
1048
+ if (!file.completeTimestamp || file.completeTimestamp > startTime) {
1049
+ loadedDuringCurrentSession += loaded;
1050
+ }
1051
+
1052
+ total.loaded += loaded;
1053
+ } else {
1054
+ total.size = undef;
1055
+ }
1056
+
1057
+ if (file.status == plupload.DONE) {
1058
+ total.uploaded++;
1059
+ } else if (file.status == plupload.FAILED) {
1060
+ total.failed++;
1061
+ } else {
1062
+ total.queued++;
1063
+ }
1064
+ }
1065
+
1066
+ // If we couldn't calculate a total file size then use the number of files to calc percent
1067
+ if (total.size === undef) {
1068
+ total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
1069
+ } else {
1070
+ total.bytesPerSec = Math.ceil(loadedDuringCurrentSession / ((+new Date() - startTime || 1) / 1000.0));
1071
+ total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
1072
+ }
1073
+ }
1074
+
1075
+
1076
+ function getRUID() {
1077
+ var ctrl = fileInputs[0] || fileDrops[0];
1078
+ if (ctrl) {
1079
+ return ctrl.getRuntime().uid;
1080
+ }
1081
+ return false;
1082
+ }
1083
+
1084
+
1085
+ function bindEventListeners() {
1086
+ this.bind('FilesAdded FilesRemoved', function(up) {
1087
+ up.trigger('QueueChanged');
1088
+ up.refresh();
1089
+ });
1090
+
1091
+ this.bind('CancelUpload', onCancelUpload);
1092
+
1093
+ this.bind('BeforeUpload', onBeforeUpload);
1094
+
1095
+ this.bind('UploadFile', onUploadFile);
1096
+
1097
+ this.bind('UploadProgress', onUploadProgress);
1098
+
1099
+ this.bind('StateChanged', onStateChanged);
1100
+
1101
+ this.bind('QueueChanged', calc);
1102
+
1103
+ this.bind('Error', onError);
1104
+
1105
+ this.bind('FileUploaded', onFileUploaded);
1106
+
1107
+ this.bind('Destroy', onDestroy);
1108
+ }
1109
+
1110
+
1111
+ function initControls(settings, cb) {
1112
+ var self = this, inited = 0, queue = [];
1113
+
1114
+ // common settings
1115
+ var options = {
1116
+ runtime_order: settings.runtimes,
1117
+ required_caps: settings.required_features,
1118
+ preferred_caps: preferred_caps,
1119
+ swf_url: settings.flash_swf_url,
1120
+ xap_url: settings.silverlight_xap_url
1121
+ };
1122
+
1123
+ // add runtime specific options if any
1124
+ plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
1125
+ if (settings[runtime]) {
1126
+ options[runtime] = settings[runtime];
1127
+ }
1128
+ });
1129
+
1130
+ // initialize file pickers - there can be many
1131
+ if (settings.browse_button) {
1132
+ plupload.each(settings.browse_button, function(el) {
1133
+ queue.push(function(cb) {
1134
+ var fileInput = new o.file.FileInput(plupload.extend({}, options, {
1135
+ accept: settings.filters.mime_types,
1136
+ name: settings.file_data_name,
1137
+ multiple: settings.multi_selection,
1138
+ container: settings.container,
1139
+ browse_button: el
1140
+ }));
1141
+
1142
+ fileInput.onready = function() {
1143
+ var info = Runtime.getInfo(this.ruid);
1144
+
1145
+ // for backward compatibility
1146
+ plupload.extend(self.features, {
1147
+ chunks: info.can('slice_blob'),
1148
+ multipart: info.can('send_multipart'),
1149
+ multi_selection: info.can('select_multiple')
1150
+ });
1151
+
1152
+ inited++;
1153
+ fileInputs.push(this);
1154
+ cb();
1155
+ };
1156
+
1157
+ fileInput.onchange = function() {
1158
+ self.addFile(this.files);
1159
+ };
1160
+
1161
+ fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
1162
+ if (!disabled) {
1163
+ if (settings.browse_button_hover) {
1164
+ if ('mouseenter' === e.type) {
1165
+ plupload.addClass(el, settings.browse_button_hover);
1166
+ } else if ('mouseleave' === e.type) {
1167
+ plupload.removeClass(el, settings.browse_button_hover);
1168
+ }
1169
+ }
1170
+
1171
+ if (settings.browse_button_active) {
1172
+ if ('mousedown' === e.type) {
1173
+ plupload.addClass(el, settings.browse_button_active);
1174
+ } else if ('mouseup' === e.type) {
1175
+ plupload.removeClass(el, settings.browse_button_active);
1176
+ }
1177
+ }
1178
+ }
1179
+ });
1180
+
1181
+ fileInput.bind('mousedown', function() {
1182
+ self.trigger('Browse');
1183
+ });
1184
+
1185
+ fileInput.bind('error runtimeerror', function() {
1186
+ fileInput = null;
1187
+ cb();
1188
+ });
1189
+
1190
+ fileInput.init();
1191
+ });
1192
+ });
1193
+ }
1194
+
1195
+ // initialize drop zones
1196
+ if (settings.drop_element) {
1197
+ plupload.each(settings.drop_element, function(el) {
1198
+ queue.push(function(cb) {
1199
+ var fileDrop = new o.file.FileDrop(plupload.extend({}, options, {
1200
+ drop_zone: el
1201
+ }));
1202
+
1203
+ fileDrop.onready = function() {
1204
+ var info = Runtime.getInfo(this.ruid);
1205
+
1206
+ // for backward compatibility
1207
+ plupload.extend(self.features, {
1208
+ chunks: info.can('slice_blob'),
1209
+ multipart: info.can('send_multipart'),
1210
+ dragdrop: info.can('drag_and_drop')
1211
+ });
1212
+
1213
+ inited++;
1214
+ fileDrops.push(this);
1215
+ cb();
1216
+ };
1217
+
1218
+ fileDrop.ondrop = function() {
1219
+ self.addFile(this.files);
1220
+ };
1221
+
1222
+ fileDrop.bind('error runtimeerror', function() {
1223
+ fileDrop = null;
1224
+ cb();
1225
+ });
1226
+
1227
+ fileDrop.init();
1228
+ });
1229
+ });
1230
+ }
1231
+
1232
+
1233
+ plupload.inSeries(queue, function() {
1234
+ if (typeof(cb) === 'function') {
1235
+ cb(inited);
1236
+ }
1237
+ });
1238
+ }
1239
+
1240
+
1241
+ function resizeImage(blob, params, runtimeOptions, cb) {
1242
+ var img = new o.image.Image();
1243
+
1244
+ try {
1245
+ img.onload = function() {
1246
+ // no manipulation required if...
1247
+ if (params.width > this.width &&
1248
+ params.height > this.height &&
1249
+ params.quality === undef &&
1250
+ params.preserve_headers &&
1251
+ !params.crop
1252
+ ) {
1253
+ this.destroy();
1254
+ cb(blob);
1255
+ } else {
1256
+ // otherwise downsize
1257
+ img.downsize(params.width, params.height, params.crop, params.preserve_headers);
1258
+ }
1259
+ };
1260
+
1261
+ img.onresize = function() {
1262
+ var resizedBlob = this.getAsBlob(blob.type, params.quality);
1263
+ this.destroy();
1264
+ cb(resizedBlob);
1265
+ };
1266
+
1267
+ img.bind('error runtimeerror', function() {
1268
+ this.destroy();
1269
+ cb(blob);
1270
+ });
1271
+
1272
+ img.load(blob, runtimeOptions);
1273
+ } catch(ex) {
1274
+ cb(blob);
1275
+ }
1276
+ }
1277
+
1278
+
1279
+ function setOption(option, value, init) {
1280
+ var self = this, reinitRequired = false;
1281
+
1282
+ function _setOption(option, value, init) {
1283
+ var oldValue = settings[option];
1284
+
1285
+ switch (option) {
1286
+ case 'max_file_size':
1287
+ if (option === 'max_file_size') {
1288
+ settings.max_file_size = settings.filters.max_file_size = value;
1289
+ }
1290
+ break;
1291
+
1292
+ case 'chunk_size':
1293
+ if (value = plupload.parseSize(value)) {
1294
+ settings[option] = value;
1295
+ settings.send_file_name = true;
1296
+ }
1297
+ break;
1298
+
1299
+ case 'multipart':
1300
+ settings[option] = value;
1301
+ if (!value) {
1302
+ settings.send_file_name = true;
1303
+ }
1304
+ break;
1305
+
1306
+ case 'http_method':
1307
+ settings[option] = value.toUpperCase() === 'PUT' ? 'PUT' : 'POST';
1308
+ break;
1309
+
1310
+ case 'unique_names':
1311
+ settings[option] = value;
1312
+ if (value) {
1313
+ settings.send_file_name = true;
1314
+ }
1315
+ break;
1316
+
1317
+ case 'filters':
1318
+ // for sake of backward compatibility
1319
+ if (plupload.typeOf(value) === 'array') {
1320
+ value = {
1321
+ mime_types: value
1322
+ };
1323
+ }
1324
+
1325
+ if (init) {
1326
+ plupload.extend(settings.filters, value);
1327
+ } else {
1328
+ settings.filters = value;
1329
+ }
1330
+
1331
+ // if file format filters are being updated, regenerate the matching expressions
1332
+ if (value.mime_types) {
1333
+ if (plupload.typeOf(value.mime_types) === 'string') {
1334
+ value.mime_types = o.core.utils.Mime.mimes2extList(value.mime_types);
1335
+ }
1336
+
1337
+ value.mime_types.regexp = (function(filters) {
1338
+ var extensionsRegExp = [];
1339
+
1340
+ plupload.each(filters, function(filter) {
1341
+ plupload.each(filter.extensions.split(/,/), function(ext) {
1342
+ if (/^\s*\*\s*$/.test(ext)) {
1343
+ extensionsRegExp.push('\\.*');
1344
+ } else {
1345
+ extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
1346
+ }
1347
+ });
1348
+ });
1349
+
1350
+ return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
1351
+ }(value.mime_types));
1352
+
1353
+ settings.filters.mime_types = value.mime_types;
1354
+ }
1355
+ break;
1356
+
1357
+ case 'resize':
1358
+ if (value) {
1359
+ settings.resize = plupload.extend({
1360
+ preserve_headers: true,
1361
+ crop: false
1362
+ }, value);
1363
+ } else {
1364
+ settings.resize = false;
1365
+ }
1366
+ break;
1367
+
1368
+ case 'prevent_duplicates':
1369
+ settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
1370
+ break;
1371
+
1372
+ // options that require reinitialisation
1373
+ case 'container':
1374
+ case 'browse_button':
1375
+ case 'drop_element':
1376
+ value = 'container' === option
1377
+ ? plupload.get(value)
1378
+ : plupload.getAll(value)
1379
+ ;
1380
+
1381
+ case 'runtimes':
1382
+ case 'multi_selection':
1383
+ case 'flash_swf_url':
1384
+ case 'silverlight_xap_url':
1385
+ settings[option] = value;
1386
+ if (!init) {
1387
+ reinitRequired = true;
1388
+ }
1389
+ break;
1390
+
1391
+ default:
1392
+ settings[option] = value;
1393
+ }
1394
+
1395
+ if (!init) {
1396
+ self.trigger('OptionChanged', option, value, oldValue);
1397
+ }
1398
+ }
1399
+
1400
+ if (typeof(option) === 'object') {
1401
+ plupload.each(option, function(value, option) {
1402
+ _setOption(option, value, init);
1403
+ });
1404
+ } else {
1405
+ _setOption(option, value, init);
1406
+ }
1407
+
1408
+ if (init) {
1409
+ // Normalize the list of required capabilities
1410
+ settings.required_features = normalizeCaps(plupload.extend({}, settings));
1411
+
1412
+ // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
1413
+ preferred_caps = normalizeCaps(plupload.extend({}, settings, {
1414
+ required_features: true
1415
+ }));
1416
+ } else if (reinitRequired) {
1417
+ self.trigger('Destroy');
1418
+
1419
+ initControls.call(self, settings, function(inited) {
1420
+ if (inited) {
1421
+ self.runtime = Runtime.getInfo(getRUID()).type;
1422
+ self.trigger('Init', { runtime: self.runtime });
1423
+ self.trigger('PostInit');
1424
+ } else {
1425
+ self.trigger('Error', {
1426
+ code : plupload.INIT_ERROR,
1427
+ message : plupload.translate('Init error.')
1428
+ });
1429
+ }
1430
+ });
1431
+ }
1432
+ }
1433
+
1434
+
1435
+ // Internal event handlers
1436
+ function onBeforeUpload(up, file) {
1437
+ // Generate unique target filenames
1438
+ if (up.settings.unique_names) {
1439
+ var matches = file.name.match(/\.([^.]+)$/), ext = "part";
1440
+ if (matches) {
1441
+ ext = matches[1];
1442
+ }
1443
+ file.target_name = file.id + '.' + ext;
1444
+ }
1445
+ }
1446
+
1447
+
1448
+ function onUploadFile(up, file) {
1449
+ var url = up.settings.url;
1450
+ var chunkSize = up.settings.chunk_size;
1451
+ var retries = up.settings.max_retries;
1452
+ var features = up.features;
1453
+ var offset = 0;
1454
+ var blob;
1455
+
1456
+ var runtimeOptions = {
1457
+ runtime_order: up.settings.runtimes,
1458
+ required_caps: up.settings.required_features,
1459
+ preferred_caps: preferred_caps,
1460
+ swf_url: up.settings.flash_swf_url,
1461
+ xap_url: up.settings.silverlight_xap_url
1462
+ };
1463
+
1464
+ // make sure we start at a predictable offset
1465
+ if (file.loaded) {
1466
+ offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
1467
+ }
1468
+
1469
+ function handleError() {
1470
+ if (retries-- > 0) {
1471
+ delay(uploadNextChunk, 1000);
1472
+ } else {
1473
+ file.loaded = offset; // reset all progress
1474
+
1475
+ up.trigger('Error', {
1476
+ code : plupload.HTTP_ERROR,
1477
+ message : plupload.translate('HTTP Error.'),
1478
+ file : file,
1479
+ response : xhr.responseText,
1480
+ status : xhr.status,
1481
+ responseHeaders: xhr.getAllResponseHeaders()
1482
+ });
1483
+ }
1484
+ }
1485
+
1486
+ function uploadNextChunk() {
1487
+ var chunkBlob, args = {}, curChunkSize;
1488
+
1489
+ // make sure that file wasn't cancelled and upload is not stopped in general
1490
+ if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
1491
+ return;
1492
+ }
1493
+
1494
+ // send additional 'name' parameter only if required
1495
+ if (up.settings.send_file_name) {
1496
+ args.name = file.target_name || file.name;
1497
+ }
1498
+
1499
+ if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
1500
+ curChunkSize = Math.min(chunkSize, blob.size - offset);
1501
+ chunkBlob = blob.slice(offset, offset + curChunkSize);
1502
+ } else {
1503
+ curChunkSize = blob.size;
1504
+ chunkBlob = blob;
1505
+ }
1506
+
1507
+ // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
1508
+ if (chunkSize && features.chunks) {
1509
+ // Setup query string arguments
1510
+ if (up.settings.send_chunk_number) {
1511
+ args.chunk = Math.ceil(offset / chunkSize);
1512
+ args.chunks = Math.ceil(blob.size / chunkSize);
1513
+ } else { // keep support for experimental chunk format, just in case
1514
+ args.offset = offset;
1515
+ args.total = blob.size;
1516
+ }
1517
+ }
1518
+
1519
+ if (up.trigger('BeforeChunkUpload', file, args, chunkBlob, offset)) {
1520
+ uploadChunk(args, chunkBlob, curChunkSize);
1521
+ }
1522
+ }
1523
+
1524
+ function uploadChunk(args, chunkBlob, curChunkSize) {
1525
+ var formData;
1526
+
1527
+ xhr = new o.xhr.XMLHttpRequest();
1528
+
1529
+ // Do we have upload progress support
1530
+ if (xhr.upload) {
1531
+ xhr.upload.onprogress = function(e) {
1532
+ file.loaded = Math.min(file.size, offset + e.loaded);
1533
+ up.trigger('UploadProgress', file);
1534
+ };
1535
+ }
1536
+
1537
+ xhr.onload = function() {
1538
+ // check if upload made itself through
1539
+ if (xhr.status < 200 || xhr.status >= 400) {
1540
+ handleError();
1541
+ return;
1542
+ }
1543
+
1544
+ retries = up.settings.max_retries; // reset the counter
1545
+
1546
+ // Handle chunk response
1547
+ if (curChunkSize < blob.size) {
1548
+ chunkBlob.destroy();
1549
+
1550
+ offset += curChunkSize;
1551
+ file.loaded = Math.min(offset, blob.size);
1552
+
1553
+ up.trigger('ChunkUploaded', file, {
1554
+ offset : file.loaded,
1555
+ total : blob.size,
1556
+ response : xhr.responseText,
1557
+ status : xhr.status,
1558
+ responseHeaders: xhr.getAllResponseHeaders()
1559
+ });
1560
+
1561
+ // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
1562
+ if (plupload.ua.browser === 'Android Browser') {
1563
+ // doesn't harm in general, but is not required anywhere else
1564
+ up.trigger('UploadProgress', file);
1565
+ }
1566
+ } else {
1567
+ file.loaded = file.size;
1568
+ }
1569
+
1570
+ chunkBlob = formData = null; // Free memory
1571
+
1572
+ // Check if file is uploaded
1573
+ if (!offset || offset >= blob.size) {
1574
+ // If file was modified, destory the copy
1575
+ if (file.size != file.origSize) {
1576
+ blob.destroy();
1577
+ blob = null;
1578
+ }
1579
+
1580
+ up.trigger('UploadProgress', file);
1581
+
1582
+ file.status = plupload.DONE;
1583
+ file.completeTimestamp = +new Date();
1584
+
1585
+ up.trigger('FileUploaded', file, {
1586
+ response : xhr.responseText,
1587
+ status : xhr.status,
1588
+ responseHeaders: xhr.getAllResponseHeaders()
1589
+ });
1590
+ } else {
1591
+ // Still chunks left
1592
+ delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
1593
+ }
1594
+ };
1595
+
1596
+ xhr.onerror = function() {
1597
+ handleError();
1598
+ };
1599
+
1600
+ xhr.onloadend = function() {
1601
+ this.destroy();
1602
+ };
1603
+
1604
+ // Build multipart request
1605
+ if (up.settings.multipart && features.multipart) {
1606
+ xhr.open(up.settings.http_method, url, true);
1607
+
1608
+ // Set custom headers
1609
+ plupload.each(up.settings.headers, function(value, name) {
1610
+ xhr.setRequestHeader(name, value);
1611
+ });
1612
+
1613
+ formData = new o.xhr.FormData();
1614
+
1615
+ // Add multipart params
1616
+ plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
1617
+ formData.append(name, value);
1618
+ });
1619
+
1620
+ // Add file and send it
1621
+ formData.append(up.settings.file_data_name, chunkBlob);
1622
+ xhr.send(formData, runtimeOptions);
1623
+ } else {
1624
+ // if no multipart, send as binary stream
1625
+ url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
1626
+
1627
+ xhr.open(up.settings.http_method, url, true);
1628
+
1629
+ // Set custom headers
1630
+ plupload.each(up.settings.headers, function(value, name) {
1631
+ xhr.setRequestHeader(name, value);
1632
+ });
1633
+
1634
+ // do not set Content-Type, if it was defined previously (see #1203)
1635
+ if (!xhr.hasRequestHeader('Content-Type')) {
1636
+ xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
1637
+ }
1638
+
1639
+ xhr.send(chunkBlob, runtimeOptions);
1640
+ }
1641
+ }
1642
+
1643
+
1644
+ blob = file.getSource();
1645
+
1646
+ // Start uploading chunks
1647
+ if (!plupload.isEmptyObj(up.settings.resize) && plupload.inArray(blob.type, ['image/jpeg', 'image/png']) !== -1) {
1648
+ // Resize if required
1649
+ resizeImage(blob, up.settings.resize, runtimeOptions, function(resizedBlob) {
1650
+ blob = resizedBlob;
1651
+ file.size = resizedBlob.size;
1652
+ uploadNextChunk();
1653
+ });
1654
+ } else {
1655
+ uploadNextChunk();
1656
+ }
1657
+ }
1658
+
1659
+
1660
+ function onUploadProgress(up, file) {
1661
+ calcFile(file);
1662
+ }
1663
+
1664
+
1665
+ function onStateChanged(up) {
1666
+ if (up.state == plupload.STARTED) {
1667
+ // Get start time to calculate bps
1668
+ startTime = (+new Date());
1669
+ } else if (up.state == plupload.STOPPED) {
1670
+ // Reset currently uploading files
1671
+ for (var i = up.files.length - 1; i >= 0; i--) {
1672
+ if (up.files[i].status == plupload.UPLOADING) {
1673
+ up.files[i].status = plupload.QUEUED;
1674
+ calc();
1675
+ }
1676
+ }
1677
+ }
1678
+ }
1679
+
1680
+
1681
+ function onCancelUpload() {
1682
+ if (xhr) {
1683
+ xhr.abort();
1684
+ }
1685
+ }
1686
+
1687
+
1688
+ function onFileUploaded(up) {
1689
+ calc();
1690
+
1691
+ // Upload next file but detach it from the error event
1692
+ // since other custom listeners might want to stop the queue
1693
+ delay(function() {
1694
+ uploadNext.call(up);
1695
+ }, 1);
1696
+ }
1697
+
1698
+
1699
+ function onError(up, err) {
1700
+ if (err.code === plupload.INIT_ERROR) {
1701
+ up.destroy();
1702
+ }
1703
+ // Set failed status if an error occured on a file
1704
+ else if (err.code === plupload.HTTP_ERROR) {
1705
+ err.file.status = plupload.FAILED;
1706
+ err.file.completeTimestamp = +new Date();
1707
+ calcFile(err.file);
1708
+
1709
+ // Upload next file but detach it from the error event
1710
+ // since other custom listeners might want to stop the queue
1711
+ if (up.state == plupload.STARTED) { // upload in progress
1712
+ up.trigger('CancelUpload');
1713
+ delay(function() {
1714
+ uploadNext.call(up);
1715
+ }, 1);
1716
+ }
1717
+ }
1718
+ }
1719
+
1720
+
1721
+ function onDestroy(up) {
1722
+ up.stop();
1723
+
1724
+ // Purge the queue
1725
+ plupload.each(files, function(file) {
1726
+ file.destroy();
1727
+ });
1728
+ files = [];
1729
+
1730
+ if (fileInputs.length) {
1731
+ plupload.each(fileInputs, function(fileInput) {
1732
+ fileInput.destroy();
1733
+ });
1734
+ fileInputs = [];
1735
+ }
1736
+
1737
+ if (fileDrops.length) {
1738
+ plupload.each(fileDrops, function(fileDrop) {
1739
+ fileDrop.destroy();
1740
+ });
1741
+ fileDrops = [];
1742
+ }
1743
+
1744
+ preferred_caps = {};
1745
+ disabled = false;
1746
+ startTime = xhr = null;
1747
+ total.reset();
1748
+ }
1749
+
1750
+
1751
+ // Default settings
1752
+ settings = {
1753
+ chunk_size: 0,
1754
+ file_data_name: 'file',
1755
+ filters: {
1756
+ mime_types: [],
1757
+ max_file_size: 0,
1758
+ prevent_duplicates: false,
1759
+ prevent_empty: true
1760
+ },
1761
+ flash_swf_url: 'js/Moxie.swf',
1762
+ http_method: 'POST',
1763
+ max_retries: 0,
1764
+ multipart: true,
1765
+ multi_selection: true,
1766
+ resize: false,
1767
+ runtimes: Runtime.order,
1768
+ send_file_name: true,
1769
+ send_chunk_number: true,
1770
+ silverlight_xap_url: 'js/Moxie.xap'
1771
+ };
1772
+
1773
+
1774
+ setOption.call(this, options, null, true);
1775
+
1776
+ // Inital total state
1777
+ total = new plupload.QueueProgress();
1778
+
1779
+ // Add public methods
1780
+ plupload.extend(this, {
1781
+
1782
+ /**
1783
+ * Unique id for the Uploader instance.
1784
+ *
1785
+ * @property id
1786
+ * @type String
1787
+ */
1788
+ id : uid,
1789
+ uid : uid, // mOxie uses this to differentiate between event targets
1790
+
1791
+ /**
1792
+ * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
1793
+ * These states are controlled by the stop/start methods. The default value is STOPPED.
1794
+ *
1795
+ * @property state
1796
+ * @type Number
1797
+ */
1798
+ state : plupload.STOPPED,
1799
+
1800
+ /**
1801
+ * Map of features that are available for the uploader runtime. Features will be filled
1802
+ * before the init event is called, these features can then be used to alter the UI for the end user.
1803
+ * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
1804
+ *
1805
+ * @property features
1806
+ * @type Object
1807
+ */
1808
+ features : {},
1809
+
1810
+ /**
1811
+ * Current runtime name.
1812
+ *
1813
+ * @property runtime
1814
+ * @type String
1815
+ */
1816
+ runtime : null,
1817
+
1818
+ /**
1819
+ * Current upload queue, an array of File instances.
1820
+ *
1821
+ * @property files
1822
+ * @type Array
1823
+ * @see plupload.File
1824
+ */
1825
+ files : files,
1826
+
1827
+ /**
1828
+ * Object with name/value settings.
1829
+ *
1830
+ * @property settings
1831
+ * @type Object
1832
+ */
1833
+ settings : settings,
1834
+
1835
+ /**
1836
+ * Total progess information. How many files has been uploaded, total percent etc.
1837
+ *
1838
+ * @property total
1839
+ * @type plupload.QueueProgress
1840
+ */
1841
+ total : total,
1842
+
1843
+
1844
+ /**
1845
+ * Initializes the Uploader instance and adds internal event listeners.
1846
+ *
1847
+ * @method init
1848
+ */
1849
+ init : function() {
1850
+ var self = this, opt, preinitOpt, err;
1851
+
1852
+ preinitOpt = self.getOption('preinit');
1853
+ if (typeof(preinitOpt) == "function") {
1854
+ preinitOpt(self);
1855
+ } else {
1856
+ plupload.each(preinitOpt, function(func, name) {
1857
+ self.bind(name, func);
1858
+ });
1859
+ }
1860
+
1861
+ bindEventListeners.call(self);
1862
+
1863
+ // Check for required options
1864
+ plupload.each(['container', 'browse_button', 'drop_element'], function(el) {
1865
+ if (self.getOption(el) === null) {
1866
+ err = {
1867
+ code : plupload.INIT_ERROR,
1868
+ message : plupload.sprintf(plupload.translate("%s specified, but cannot be found."), el)
1869
+ }
1870
+ return false;
1871
+ }
1872
+ });
1873
+
1874
+ if (err) {
1875
+ return self.trigger('Error', err);
1876
+ }
1877
+
1878
+
1879
+ if (!settings.browse_button && !settings.drop_element) {
1880
+ return self.trigger('Error', {
1881
+ code : plupload.INIT_ERROR,
1882
+ message : plupload.translate("You must specify either browse_button or drop_element.")
1883
+ });
1884
+ }
1885
+
1886
+
1887
+ initControls.call(self, settings, function(inited) {
1888
+ var initOpt = self.getOption('init');
1889
+ if (typeof(initOpt) == "function") {
1890
+ initOpt(self);
1891
+ } else {
1892
+ plupload.each(initOpt, function(func, name) {
1893
+ self.bind(name, func);
1894
+ });
1895
+ }
1896
+
1897
+ if (inited) {
1898
+ self.runtime = Runtime.getInfo(getRUID()).type;
1899
+ self.trigger('Init', { runtime: self.runtime });
1900
+ self.trigger('PostInit');
1901
+ } else {
1902
+ self.trigger('Error', {
1903
+ code : plupload.INIT_ERROR,
1904
+ message : plupload.translate('Init error.')
1905
+ });
1906
+ }
1907
+ });
1908
+ },
1909
+
1910
+ /**
1911
+ * Set the value for the specified option(s).
1912
+ *
1913
+ * @method setOption
1914
+ * @since 2.1
1915
+ * @param {String|Object} option Name of the option to change or the set of key/value pairs
1916
+ * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
1917
+ */
1918
+ setOption: function(option, value) {
1919
+ setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
1920
+ },
1921
+
1922
+ /**
1923
+ * Get the value for the specified option or the whole configuration, if not specified.
1924
+ *
1925
+ * @method getOption
1926
+ * @since 2.1
1927
+ * @param {String} [option] Name of the option to get
1928
+ * @return {Mixed} Value for the option or the whole set
1929
+ */
1930
+ getOption: function(option) {
1931
+ if (!option) {
1932
+ return settings;
1933
+ }
1934
+ return settings[option];
1935
+ },
1936
+
1937
+ /**
1938
+ * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
1939
+ * This would for example reposition flash/silverlight shims on the page.
1940
+ *
1941
+ * @method refresh
1942
+ */
1943
+ refresh : function() {
1944
+ if (fileInputs.length) {
1945
+ plupload.each(fileInputs, function(fileInput) {
1946
+ fileInput.trigger('Refresh');
1947
+ });
1948
+ }
1949
+ this.trigger('Refresh');
1950
+ },
1951
+
1952
+ /**
1953
+ * Starts uploading the queued files.
1954
+ *
1955
+ * @method start
1956
+ */
1957
+ start : function() {
1958
+ if (this.state != plupload.STARTED) {
1959
+ this.state = plupload.STARTED;
1960
+ this.trigger('StateChanged');
1961
+
1962
+ uploadNext.call(this);
1963
+ }
1964
+ },
1965
+
1966
+ /**
1967
+ * Stops the upload of the queued files.
1968
+ *
1969
+ * @method stop
1970
+ */
1971
+ stop : function() {
1972
+ if (this.state != plupload.STOPPED) {
1973
+ this.state = plupload.STOPPED;
1974
+ this.trigger('StateChanged');
1975
+ this.trigger('CancelUpload');
1976
+ }
1977
+ },
1978
+
1979
+
1980
+ /**
1981
+ * Disables/enables browse button on request.
1982
+ *
1983
+ * @method disableBrowse
1984
+ * @param {Boolean} disable Whether to disable or enable (default: true)
1985
+ */
1986
+ disableBrowse : function() {
1987
+ disabled = arguments[0] !== undef ? arguments[0] : true;
1988
+
1989
+ if (fileInputs.length) {
1990
+ plupload.each(fileInputs, function(fileInput) {
1991
+ fileInput.disable(disabled);
1992
+ });
1993
+ }
1994
+
1995
+ this.trigger('DisableBrowse', disabled);
1996
+ },
1997
+
1998
+ /**
1999
+ * Returns the specified file object by id.
2000
+ *
2001
+ * @method getFile
2002
+ * @param {String} id File id to look for.
2003
+ * @return {plupload.File} File object or undefined if it wasn't found;
2004
+ */
2005
+ getFile : function(id) {
2006
+ var i;
2007
+ for (i = files.length - 1; i >= 0; i--) {
2008
+ if (files[i].id === id) {
2009
+ return files[i];
2010
+ }
2011
+ }
2012
+ },
2013
+
2014
+ /**
2015
+ * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
2016
+ * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
2017
+ * if any files were added to the queue. Otherwise nothing happens.
2018
+ *
2019
+ * @method addFile
2020
+ * @since 2.0
2021
+ * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
2022
+ * @param {String} [fileName] If specified, will be used as a name for the file
2023
+ */
2024
+ addFile : function(file, fileName) {
2025
+ var self = this
2026
+ , queue = []
2027
+ , filesAdded = []
2028
+ , ruid
2029
+ ;
2030
+
2031
+ function filterFile(file, cb) {
2032
+ var queue = [];
2033
+ plupload.each(self.settings.filters, function(rule, name) {
2034
+ if (fileFilters[name]) {
2035
+ queue.push(function(cb) {
2036
+ fileFilters[name].call(self, rule, file, function(res) {
2037
+ cb(!res);
2038
+ });
2039
+ });
2040
+ }
2041
+ });
2042
+ plupload.inSeries(queue, cb);
2043
+ }
2044
+
2045
+ /**
2046
+ * @method resolveFile
2047
+ * @private
2048
+ * @param {moxie.file.File|moxie.file.Blob|plupload.File|File|Blob|input[type="file"]} file
2049
+ */
2050
+ function resolveFile(file) {
2051
+ var type = plupload.typeOf(file);
2052
+
2053
+ // moxie.file.File
2054
+ if (file instanceof o.file.File) {
2055
+ if (!file.ruid && !file.isDetached()) {
2056
+ if (!ruid) { // weird case
2057
+ return false;
2058
+ }
2059
+ file.ruid = ruid;
2060
+ file.connectRuntime(ruid);
2061
+ }
2062
+ resolveFile(new plupload.File(file));
2063
+ }
2064
+ // moxie.file.Blob
2065
+ else if (file instanceof o.file.Blob) {
2066
+ resolveFile(file.getSource());
2067
+ file.destroy();
2068
+ }
2069
+ // plupload.File - final step for other branches
2070
+ else if (file instanceof plupload.File) {
2071
+ if (fileName) {
2072
+ file.name = fileName;
2073
+ }
2074
+
2075
+ queue.push(function(cb) {
2076
+ // run through the internal and user-defined filters, if any
2077
+ filterFile(file, function(err) {
2078
+ if (!err) {
2079
+ // make files available for the filters by updating the main queue directly
2080
+ files.push(file);
2081
+ // collect the files that will be passed to FilesAdded event
2082
+ filesAdded.push(file);
2083
+
2084
+ self.trigger("FileFiltered", file);
2085
+ }
2086
+ delay(cb, 1); // do not build up recursions or eventually we might hit the limits
2087
+ });
2088
+ });
2089
+ }
2090
+ // native File or blob
2091
+ else if (plupload.inArray(type, ['file', 'blob']) !== -1) {
2092
+ resolveFile(new o.file.File(null, file));
2093
+ }
2094
+ // input[type="file"]
2095
+ else if (type === 'node' && plupload.typeOf(file.files) === 'filelist') {
2096
+ // if we are dealing with input[type="file"]
2097
+ plupload.each(file.files, resolveFile);
2098
+ }
2099
+ // mixed array of any supported types (see above)
2100
+ else if (type === 'array') {
2101
+ fileName = null; // should never happen, but unset anyway to avoid funny situations
2102
+ plupload.each(file, resolveFile);
2103
+ }
2104
+ }
2105
+
2106
+ ruid = getRUID();
2107
+
2108
+ resolveFile(file);
2109
+
2110
+ if (queue.length) {
2111
+ plupload.inSeries(queue, function() {
2112
+ // if any files left after filtration, trigger FilesAdded
2113
+ if (filesAdded.length) {
2114
+ self.trigger("FilesAdded", filesAdded);
2115
+ }
2116
+ });
2117
+ }
2118
+ },
2119
+
2120
+ /**
2121
+ * Removes a specific file.
2122
+ *
2123
+ * @method removeFile
2124
+ * @param {plupload.File|String} file File to remove from queue.
2125
+ */
2126
+ removeFile : function(file) {
2127
+ var id = typeof(file) === 'string' ? file : file.id;
2128
+
2129
+ for (var i = files.length - 1; i >= 0; i--) {
2130
+ if (files[i].id === id) {
2131
+ return this.splice(i, 1)[0];
2132
+ }
2133
+ }
2134
+ },
2135
+
2136
+ /**
2137
+ * Removes part of the queue and returns the files removed. This will also trigger the
2138
+ * FilesRemoved and QueueChanged events.
2139
+ *
2140
+ * @method splice
2141
+ * @param {Number} [start=0] Start index to remove from.
2142
+ * @param {Number} [length] Number of files to remove (defaults to number of files in the queue).
2143
+ * @return {Array} Array of files that was removed.
2144
+ */
2145
+ splice : function(start, length) {
2146
+ // Splice and trigger events
2147
+ var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
2148
+
2149
+ // if upload is in progress we need to stop it and restart after files are removed
2150
+ var restartRequired = false;
2151
+ if (this.state == plupload.STARTED) { // upload in progress
2152
+ plupload.each(removed, function(file) {
2153
+ if (file.status === plupload.UPLOADING) {
2154
+ restartRequired = true; // do not restart, unless file that is being removed is uploading
2155
+ return false;
2156
+ }
2157
+ });
2158
+
2159
+ if (restartRequired) {
2160
+ this.stop();
2161
+ }
2162
+ }
2163
+
2164
+ this.trigger("FilesRemoved", removed);
2165
+
2166
+ // Dispose any resources allocated by those files
2167
+ plupload.each(removed, function(file) {
2168
+ file.destroy();
2169
+ });
2170
+
2171
+ if (restartRequired) {
2172
+ this.start();
2173
+ }
2174
+
2175
+ return removed;
2176
+ },
2177
+
2178
+ /**
2179
+ Dispatches the specified event name and its arguments to all listeners.
2180
+
2181
+ @method trigger
2182
+ @param {String} name Event name to fire.
2183
+ @param {Object..} Multiple arguments to pass along to the listener functions.
2184
+ */
2185
+
2186
+ // override the parent method to match Plupload-like event logic
2187
+ dispatchEvent: function(type) {
2188
+ var list, args, result;
2189
+
2190
+ type = type.toLowerCase();
2191
+
2192
+ list = this.hasEventListener(type);
2193
+
2194
+ if (list) {
2195
+ // sort event list by priority
2196
+ list.sort(function(a, b) { return b.priority - a.priority; });
2197
+
2198
+ // first argument should be current plupload.Uploader instance
2199
+ args = [].slice.call(arguments);
2200
+ args.shift();
2201
+ args.unshift(this);
2202
+
2203
+ for (var i = 0; i < list.length; i++) {
2204
+ // Fire event, break chain if false is returned
2205
+ if (list[i].fn.apply(list[i].scope, args) === false) {
2206
+ return false;
2207
+ }
2208
+ }
2209
+ }
2210
+ return true;
2211
+ },
2212
+
2213
+ /**
2214
+ Check whether uploader has any listeners to the specified event.
2215
+
2216
+ @method hasEventListener
2217
+ @param {String} name Event name to check for.
2218
+ */
2219
+
2220
+
2221
+ /**
2222
+ Adds an event listener by name.
2223
+
2224
+ @method bind
2225
+ @param {String} name Event name to listen for.
2226
+ @param {function} fn Function to call ones the event gets fired.
2227
+ @param {Object} [scope] Optional scope to execute the specified function in.
2228
+ @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
2229
+ */
2230
+ bind: function(name, fn, scope, priority) {
2231
+ // adapt moxie EventTarget style to Plupload-like
2232
+ plupload.Uploader.prototype.bind.call(this, name, fn, priority, scope);
2233
+ },
2234
+
2235
+ /**
2236
+ Removes the specified event listener.
2237
+
2238
+ @method unbind
2239
+ @param {String} name Name of event to remove.
2240
+ @param {function} fn Function to remove from listener.
2241
+ */
2242
+
2243
+ /**
2244
+ Removes all event listeners.
2245
+
2246
+ @method unbindAll
2247
+ */
2248
+
2249
+
2250
+ /**
2251
+ * Destroys Plupload instance and cleans after itself.
2252
+ *
2253
+ * @method destroy
2254
+ */
2255
+ destroy : function() {
2256
+ this.trigger('Destroy');
2257
+ settings = total = null; // purge these exclusively
2258
+ this.unbindAll();
2259
+ }
2260
+ });
2261
+ };
2262
+
2263
+ plupload.Uploader.prototype = o.core.EventTarget.instance;
2264
+
2265
+ /**
2266
+ * Constructs a new file instance.
2267
+ *
2268
+ * @class File
2269
+ * @constructor
2270
+ *
2271
+ * @param {Object} file Object containing file properties
2272
+ * @param {String} file.name Name of the file.
2273
+ * @param {Number} file.size File size.
2274
+ */
2275
+ plupload.File = (function() {
2276
+ var filepool = {};
2277
+
2278
+ function PluploadFile(file) {
2279
+
2280
+ plupload.extend(this, {
2281
+
2282
+ /**
2283
+ * File id this is a globally unique id for the specific file.
2284
+ *
2285
+ * @property id
2286
+ * @type String
2287
+ */
2288
+ id: plupload.guid(),
2289
+
2290
+ /**
2291
+ * File name for example "myfile.gif".
2292
+ *
2293
+ * @property name
2294
+ * @type String
2295
+ */
2296
+ name: file.name || file.fileName,
2297
+
2298
+ /**
2299
+ * File type, `e.g image/jpeg`
2300
+ *
2301
+ * @property type
2302
+ * @type String
2303
+ */
2304
+ type: file.type || '',
2305
+
2306
+ /**
2307
+ * Relative path to the file inside a directory
2308
+ *
2309
+ * @property relativePath
2310
+ * @type String
2311
+ * @default ''
2312
+ */
2313
+ relativePath: file.relativePath || '',
2314
+
2315
+ /**
2316
+ * File size in bytes (may change after client-side manupilation).
2317
+ *
2318
+ * @property size
2319
+ * @type Number
2320
+ */
2321
+ size: file.fileSize || file.size,
2322
+
2323
+ /**
2324
+ * Original file size in bytes.
2325
+ *
2326
+ * @property origSize
2327
+ * @type Number
2328
+ */
2329
+ origSize: file.fileSize || file.size,
2330
+
2331
+ /**
2332
+ * Number of bytes uploaded of the files total size.
2333
+ *
2334
+ * @property loaded
2335
+ * @type Number
2336
+ */
2337
+ loaded: 0,
2338
+
2339
+ /**
2340
+ * Number of percentage uploaded of the file.
2341
+ *
2342
+ * @property percent
2343
+ * @type Number
2344
+ */
2345
+ percent: 0,
2346
+
2347
+ /**
2348
+ * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
2349
+ *
2350
+ * @property status
2351
+ * @type Number
2352
+ * @see plupload
2353
+ */
2354
+ status: plupload.QUEUED,
2355
+
2356
+ /**
2357
+ * Date of last modification.
2358
+ *
2359
+ * @property lastModifiedDate
2360
+ * @type {String}
2361
+ */
2362
+ lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
2363
+
2364
+
2365
+ /**
2366
+ * Set when file becomes plupload.DONE or plupload.FAILED. Is used to calculate proper plupload.QueueProgress.bytesPerSec.
2367
+ * @private
2368
+ * @property completeTimestamp
2369
+ * @type {Number}
2370
+ */
2371
+ completeTimestamp: 0,
2372
+
2373
+ /**
2374
+ * Returns native window.File object, when it's available.
2375
+ *
2376
+ * @method getNative
2377
+ * @return {window.File} or null, if plupload.File is of different origin
2378
+ */
2379
+ getNative: function() {
2380
+ var file = this.getSource().getSource();
2381
+ return plupload.inArray(plupload.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
2382
+ },
2383
+
2384
+ /**
2385
+ * Returns mOxie.File - unified wrapper object that can be used across runtimes.
2386
+ *
2387
+ * @method getSource
2388
+ * @return {mOxie.File} or null
2389
+ */
2390
+ getSource: function() {
2391
+ if (!filepool[this.id]) {
2392
+ return null;
2393
+ }
2394
+ return filepool[this.id];
2395
+ },
2396
+
2397
+ /**
2398
+ * Destroys plupload.File object.
2399
+ *
2400
+ * @method destroy
2401
+ */
2402
+ destroy: function() {
2403
+ var src = this.getSource();
2404
+ if (src) {
2405
+ src.destroy();
2406
+ delete filepool[this.id];
2407
+ }
2408
+ }
2409
+ });
2410
+
2411
+ filepool[this.id] = file;
2412
+ }
2413
+
2414
+ return PluploadFile;
2415
+ }());
2416
+
2417
+
2418
+ /**
2419
+ * Constructs a queue progress.
2420
+ *
2421
+ * @class QueueProgress
2422
+ * @constructor
2423
+ */
2424
+ plupload.QueueProgress = function() {
2425
+ var self = this; // Setup alias for self to reduce code size when it's compressed
2426
+
2427
+ /**
2428
+ * Total queue file size.
2429
+ *
2430
+ * @property size
2431
+ * @type Number
2432
+ */
2433
+ self.size = 0;
2434
+
2435
+ /**
2436
+ * Total bytes uploaded.
2437
+ *
2438
+ * @property loaded
2439
+ * @type Number
2440
+ */
2441
+ self.loaded = 0;
2442
+
2443
+ /**
2444
+ * Number of files uploaded.
2445
+ *
2446
+ * @property uploaded
2447
+ * @type Number
2448
+ */
2449
+ self.uploaded = 0;
2450
+
2451
+ /**
2452
+ * Number of files failed to upload.
2453
+ *
2454
+ * @property failed
2455
+ * @type Number
2456
+ */
2457
+ self.failed = 0;
2458
+
2459
+ /**
2460
+ * Number of files yet to be uploaded.
2461
+ *
2462
+ * @property queued
2463
+ * @type Number
2464
+ */
2465
+ self.queued = 0;
2466
+
2467
+ /**
2468
+ * Total percent of the uploaded bytes.
2469
+ *
2470
+ * @property percent
2471
+ * @type Number
2472
+ */
2473
+ self.percent = 0;
2474
+
2475
+ /**
2476
+ * Bytes uploaded per second.
2477
+ *
2478
+ * @property bytesPerSec
2479
+ * @type Number
2480
+ */
2481
+ self.bytesPerSec = 0;
2482
+
2483
+ /**
2484
+ * Resets the progress to its initial values.
2485
+ *
2486
+ * @method reset
2487
+ */
2488
+ self.reset = function() {
2489
+ self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
2490
+ };
2491
+ };
2492
+
2493
+ exports.plupload = plupload;
2494
+
2495
+ }(this, moxie));
2496
+
2497
+ }));