Make WordPress Core

Ticket #48277: 48277.diff

File 48277.diff, 372.0 KB (added by Hareesh Pillai, 6 years ago)
  • src/js/_enqueues/vendor/plupload/license.txt

     
    1                     GNU GENERAL PUBLIC LICENSE
    2                        Version 2, June 1991
     1GNU AFFERO GENERAL PUBLIC LICENSE
     2   Version 3, 19 November 2007
    33
    4  Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
    5  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    6  Everyone is permitted to copy and distribute verbatim copies
    7  of this license document, but changing it is not allowed.
     4Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
     5Everyone is permitted to copy and distribute verbatim copies
     6of this license document, but changing it is not allowed.
    87
    9                             Preamble
     8                Preamble
    109
    11   The licenses for most software are designed to take away your
    12 freedom to share and change it.  By contrast, the GNU General Public
    13 License is intended to guarantee your freedom to share and change free
    14 software--to make sure the software is free for all its users.  This
    15 General Public License applies to most of the Free Software
    16 Foundation's software and to any other program whose authors commit to
    17 using it.  (Some other Free Software Foundation software is covered by
    18 the GNU Lesser General Public License instead.)  You can apply it to
    19 your programs, too.
     10The GNU Affero General Public License is a free, copyleft license for
     11software and other kinds of works, specifically designed to ensure
     12cooperation with the community in the case of network server software.
    2013
    21   When we speak of free software, we are referring to freedom, not
     14The licenses for most software and other practical works are designed
     15to take away your freedom to share and change the works.  By contrast,
     16our General Public Licenses are intended to guarantee your freedom to
     17share and change all versions of a program--to make sure it remains free
     18software for all its users.
     19
     20When we speak of free software, we are referring to freedom, not
    2221price.  Our General Public Licenses are designed to make sure that you
    2322have the freedom to distribute copies of free software (and charge for
    24 this service if you wish), that you receive source code or can get it
    25 if you want it, that you can change the software or use pieces of it
    26 in new free programs; and that you know you can do these things.
     23them if you wish), that you receive source code or can get it if you
     24want it, that you can change the software or use pieces of it in new
     25free programs, and that you know you can do these things.
    2726
    28   To protect your rights, we need to make restrictions that forbid
    29 anyone to deny you these rights or to ask you to surrender the rights.
    30 These restrictions translate to certain responsibilities for you if you
    31 distribute copies of the software, or if you modify it.
     27Developers that use our General Public Licenses protect your rights
     28with two steps: (1) assert copyright on the software, and (2) offer
     29you this License which gives you legal permission to copy, distribute
     30and/or modify the software.
    3231
    33   For example, if you distribute copies of such a program, whether
    34 gratis or for a fee, you must give the recipients all the rights that
    35 you have.  You must make sure that they, too, receive or can get the
    36 source code.  And you must show them these terms so they know their
    37 rights.
     32A secondary benefit of defending all users' freedom is that
     33improvements made in alternate versions of the program, if they
     34receive widespread use, become available for other developers to
     35incorporate.  Many developers of free software are heartened and
     36encouraged by the resulting cooperation.  However, in the case of
     37software used on network servers, this result may fail to come about.
     38The GNU General Public License permits making a modified version and
     39letting the public access it on a server without ever releasing its
     40source code to the public.
    3841
    39   We protect your rights with two steps: (1) copyright the software, and
    40 (2) offer you this license which gives you legal permission to copy,
    41 distribute and/or modify the software.
     42The GNU Affero General Public License is designed specifically to
     43ensure that, in such cases, the modified source code becomes available
     44to the community.  It requires the operator of a network server to
     45provide the source code of the modified version running there to the
     46users of that server.  Therefore, public use of a modified version, on
     47a publicly accessible server, gives the public access to the source
     48code of the modified version.
    4249
    43   Also, for each author's protection and ours, we want to make certain
    44 that everyone understands that there is no warranty for this free
    45 software.  If the software is modified by someone else and passed on, we
    46 want its recipients to know that what they have is not the original, so
    47 that any problems introduced by others will not reflect on the original
    48 authors' reputations.
     50An older license, called the Affero General Public License and
     51published by Affero, was designed to accomplish similar goals.  This is
     52a different license, not a version of the Affero GPL, but Affero has
     53released a new version of the Affero GPL which permits relicensing under
     54this license.
    4955
    50   Finally, any free program is threatened constantly by software
    51 patents.  We wish to avoid the danger that redistributors of a free
    52 program will individually obtain patent licenses, in effect making the
    53 program proprietary.  To prevent this, we have made it clear that any
    54 patent must be licensed for everyone's free use or not licensed at all.
    55 
    56   The precise terms and conditions for copying, distribution and
     56The precise terms and conditions for copying, distribution and
    5757modification follow.
    5858
    59                     GNU GENERAL PUBLIC LICENSE
    60    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
     59   TERMS AND CONDITIONS
    6160
    62   0. This License applies to any program or other work which contains
    63 a notice placed by the copyright holder saying it may be distributed
    64 under the terms of this General Public License.  The "Program", below,
    65 refers to any such program or work, and a "work based on the Program"
    66 means either the Program or any derivative work under copyright law:
    67 that is to say, a work containing the Program or a portion of it,
    68 either verbatim or with modifications and/or translated into another
    69 language.  (Hereinafter, translation is included without limitation in
    70 the term "modification".)  Each licensee is addressed as "you".
     610. Definitions.
    7162
    72 Activities other than copying, distribution and modification are not
    73 covered by this License; they are outside its scope.  The act of
    74 running the Program is not restricted, and the output from the Program
    75 is covered only if its contents constitute a work based on the
    76 Program (independent of having been made by running the Program).
    77 Whether that is true depends on what the Program does.
     63"This License" refers to version 3 of the GNU Affero General Public License.
    7864
    79   1. You may copy and distribute verbatim copies of the Program's
    80 source code as you receive it, in any medium, provided that you
    81 conspicuously and appropriately publish on each copy an appropriate
    82 copyright notice and disclaimer of warranty; keep intact all the
    83 notices that refer to this License and to the absence of any warranty;
    84 and give any other recipients of the Program a copy of this License
    85 along with the Program.
     65"Copyright" also means copyright-like laws that apply to other kinds of
     66works, such as semiconductor masks.
    8667
    87 You may charge a fee for the physical act of transferring a copy, and
    88 you may at your option offer warranty protection in exchange for a fee.
     68"The Program" refers to any copyrightable work licensed under this
     69License.  Each licensee is addressed as "you".  "Licensees" and
     70"recipients" may be individuals or organizations.
    8971
    90   2. You may modify your copy or copies of the Program or any portion
    91 of it, thus forming a work based on the Program, and copy and
    92 distribute such modifications or work under the terms of Section 1
    93 above, provided that you also meet all of these conditions:
     72To "modify" a work means to copy from or adapt all or part of the work
     73in a fashion requiring copyright permission, other than the making of an
     74exact copy.  The resulting work is called a "modified version" of the
     75earlier work or a work "based on" the earlier work.
    9476
    95     a) You must cause the modified files to carry prominent notices
    96     stating that you changed the files and the date of any change.
     77A "covered work" means either the unmodified Program or a work based
     78on the Program.
    9779
    98     b) You must cause any work that you distribute or publish, that in
    99     whole or in part contains or is derived from the Program or any
    100     part thereof, to be licensed as a whole at no charge to all third
    101     parties under the terms of this License.
     80To "propagate" a work means to do anything with it that, without
     81permission, would make you directly or secondarily liable for
     82infringement under applicable copyright law, except executing it on a
     83computer or modifying a private copy.  Propagation includes copying,
     84distribution (with or without modification), making available to the
     85public, and in some countries other activities as well.
    10286
    103     c) If the modified program normally reads commands interactively
    104     when run, you must cause it, when started running for such
    105     interactive use in the most ordinary way, to print or display an
    106     announcement including an appropriate copyright notice and a
    107     notice that there is no warranty (or else, saying that you provide
    108     a warranty) and that users may redistribute the program under
    109     these conditions, and telling the user how to view a copy of this
    110     License.  (Exception: if the Program itself is interactive but
    111     does not normally print such an announcement, your work based on
    112     the Program is not required to print an announcement.)
     87To "convey" a work means any kind of propagation that enables other
     88parties to make or receive copies.  Mere interaction with a user through
     89a computer network, with no transfer of a copy, is not conveying.
    11390
    114 These requirements apply to the modified work as a whole.  If
    115 identifiable sections of that work are not derived from the Program,
    116 and can be reasonably considered independent and separate works in
    117 themselves, then this License, and its terms, do not apply to those
    118 sections when you distribute them as separate works.  But when you
    119 distribute the same sections as part of a whole which is a work based
    120 on the Program, the distribution of the whole must be on the terms of
    121 this License, whose permissions for other licensees extend to the
    122 entire whole, and thus to each and every part regardless of who wrote it.
     91An interactive user interface displays "Appropriate Legal Notices"
     92to the extent that it includes a convenient and prominently visible
     93feature that (1) displays an appropriate copyright notice, and (2)
     94tells the user that there is no warranty for the work (except to the
     95extent that warranties are provided), that licensees may convey the
     96work under this License, and how to view a copy of this License.  If
     97the interface presents a list of user commands or options, such as a
     98menu, a prominent item in the list meets this criterion.
    12399
    124 Thus, it is not the intent of this section to claim rights or contest
    125 your rights to work written entirely by you; rather, the intent is to
    126 exercise the right to control the distribution of derivative or
    127 collective works based on the Program.
     1001. Source Code.
    128101
    129 In addition, mere aggregation of another work not based on the Program
    130 with the Program (or with a work based on the Program) on a volume of
    131 a storage or distribution medium does not bring the other work under
    132 the scope of this License.
     102The "source code" for a work means the preferred form of the work
     103for making modifications to it.  "Object code" means any non-source
     104form of a work.
    133105
    134   3. You may copy and distribute the Program (or a work based on it,
    135 under Section 2) in object code or executable form under the terms of
    136 Sections 1 and 2 above provided that you also do one of the following:
     106A "Standard Interface" means an interface that either is an official
     107standard defined by a recognized standards body, or, in the case of
     108interfaces specified for a particular programming language, one that
     109is widely used among developers working in that language.
    137110
    138     a) Accompany it with the complete corresponding machine-readable
    139     source code, which must be distributed under the terms of Sections
    140     1 and 2 above on a medium customarily used for software interchange; or,
     111The "System Libraries" of an executable work include anything, other
     112than the work as a whole, that (a) is included in the normal form of
     113packaging a Major Component, but which is not part of that Major
     114Component, and (b) serves only to enable use of the work with that
     115Major Component, or to implement a Standard Interface for which an
     116implementation is available to the public in source code form.  A
     117"Major Component", in this context, means a major essential component
     118(kernel, window system, and so on) of the specific operating system
     119(if any) on which the executable work runs, or a compiler used to
     120produce the work, or an object code interpreter used to run it.
    141121
    142     b) Accompany it with a written offer, valid for at least three
    143     years, to give any third party, for a charge no more than your
    144     cost of physically performing source distribution, a complete
    145     machine-readable copy of the corresponding source code, to be
    146     distributed under the terms of Sections 1 and 2 above on a medium
    147     customarily used for software interchange; or,
     122The "Corresponding Source" for a work in object code form means all
     123the source code needed to generate, install, and (for an executable
     124work) run the object code and to modify the work, including scripts to
     125control those activities.  However, it does not include the work's
     126System Libraries, or general-purpose tools or generally available free
     127programs which are used unmodified in performing those activities but
     128which are not part of the work.  For example, Corresponding Source
     129includes interface definition files associated with source files for
     130the work, and the source code for shared libraries and dynamically
     131linked subprograms that the work is specifically designed to require,
     132such as by intimate data communication or control flow between those
     133subprograms and other parts of the work.
    148134
    149     c) Accompany it with the information you received as to the offer
    150     to distribute corresponding source code.  (This alternative is
    151     allowed only for noncommercial distribution and only if you
    152     received the program in object code or executable form with such
    153     an offer, in accord with Subsection b above.)
     135The Corresponding Source need not include anything that users
     136can regenerate automatically from other parts of the Corresponding
     137Source.
    154138
    155 The source code for a work means the preferred form of the work for
    156 making modifications to it.  For an executable work, complete source
    157 code means all the source code for all modules it contains, plus any
    158 associated interface definition files, plus the scripts used to
    159 control compilation and installation of the executable.  However, as a
    160 special exception, the source code distributed need not include
    161 anything that is normally distributed (in either source or binary
    162 form) with the major components (compiler, kernel, and so on) of the
    163 operating system on which the executable runs, unless that component
    164 itself accompanies the executable.
     139The Corresponding Source for a work in source code form is that
     140same work.
    165141
    166 If distribution of executable or object code is made by offering
    167 access to copy from a designated place, then offering equivalent
    168 access to copy the source code from the same place counts as
    169 distribution of the source code, even though third parties are not
    170 compelled to copy the source along with the object code.
     1422. Basic Permissions.
    171143
    172   4. You may not copy, modify, sublicense, or distribute the Program
    173 except as expressly provided under this License.  Any attempt
    174 otherwise to copy, modify, sublicense or distribute the Program is
    175 void, and will automatically terminate your rights under this License.
    176 However, parties who have received copies, or rights, from you under
    177 this License will not have their licenses terminated so long as such
    178 parties remain in full compliance.
     144All rights granted under this License are granted for the term of
     145copyright on the Program, and are irrevocable provided the stated
     146conditions are met.  This License explicitly affirms your unlimited
     147permission to run the unmodified Program.  The output from running a
     148covered work is covered by this License only if the output, given its
     149content, constitutes a covered work.  This License acknowledges your
     150rights of fair use or other equivalent, as provided by copyright law.
    179151
    180   5. You are not required to accept this License, since you have not
    181 signed it.  However, nothing else grants you permission to modify or
    182 distribute the Program or its derivative works.  These actions are
    183 prohibited by law if you do not accept this License.  Therefore, by
    184 modifying or distributing the Program (or any work based on the
    185 Program), you indicate your acceptance of this License to do so, and
    186 all its terms and conditions for copying, distributing or modifying
    187 the Program or works based on it.
     152You may make, run and propagate covered works that you do not
     153convey, without conditions so long as your license otherwise remains
     154in force.  You may convey covered works to others for the sole purpose
     155of having them make modifications exclusively for you, or provide you
     156with facilities for running those works, provided that you comply with
     157the terms of this License in conveying all material for which you do
     158not control copyright.  Those thus making or running the covered works
     159for you must do so exclusively on your behalf, under your direction
     160and control, on terms that prohibit them from making any copies of
     161your copyrighted material outside their relationship with you.
    188162
    189   6. Each time you redistribute the Program (or any work based on the
    190 Program), the recipient automatically receives a license from the
    191 original licensor to copy, distribute or modify the Program subject to
    192 these terms and conditions.  You may not impose any further
    193 restrictions on the recipients' exercise of the rights granted herein.
    194 You are not responsible for enforcing compliance by third parties to
     163Conveying under any other circumstances is permitted solely under
     164the conditions stated below.  Sublicensing is not allowed; section 10
     165makes it unnecessary.
     166
     1673. Protecting Users' Legal Rights From Anti-Circumvention Law.
     168
     169No covered work shall be deemed part of an effective technological
     170measure under any applicable law fulfilling obligations under article
     17111 of the WIPO copyright treaty adopted on 20 December 1996, or
     172similar laws prohibiting or restricting circumvention of such
     173measures.
     174
     175When you convey a covered work, you waive any legal power to forbid
     176circumvention of technological measures to the extent such circumvention
     177is effected by exercising rights under this License with respect to
     178the covered work, and you disclaim any intention to limit operation or
     179modification of the work as a means of enforcing, against the work's
     180users, your or third parties' legal rights to forbid circumvention of
     181technological measures.
     182
     1834. Conveying Verbatim Copies.
     184
     185You may convey verbatim copies of the Program's source code as you
     186receive it, in any medium, provided that you conspicuously and
     187appropriately publish on each copy an appropriate copyright notice;
     188keep intact all notices stating that this License and any
     189non-permissive terms added in accord with section 7 apply to the code;
     190keep intact all notices of the absence of any warranty; and give all
     191recipients a copy of this License along with the Program.
     192
     193You may charge any price or no price for each copy that you convey,
     194and you may offer support or warranty protection for a fee.
     195
     1965. Conveying Modified Source Versions.
     197
     198You may convey a work based on the Program, or the modifications to
     199produce it from the Program, in the form of source code under the
     200terms of section 4, provided that you also meet all of these conditions:
     201
     202a) The work must carry prominent notices stating that you modified
     203it, and giving a relevant date.
     204
     205b) The work must carry prominent notices stating that it is
     206released under this License and any conditions added under section
     2077.  This requirement modifies the requirement in section 4 to
     208"keep intact all notices".
     209
     210c) You must license the entire work, as a whole, under this
     211License to anyone who comes into possession of a copy.  This
     212License will therefore apply, along with any applicable section 7
     213additional terms, to the whole of the work, and all its parts,
     214regardless of how they are packaged.  This License gives no
     215permission to license the work in any other way, but it does not
     216invalidate such permission if you have separately received it.
     217
     218d) If the work has interactive user interfaces, each must display
     219Appropriate Legal Notices; however, if the Program has interactive
     220interfaces that do not display Appropriate Legal Notices, your
     221work need not make them do so.
     222
     223A compilation of a covered work with other separate and independent
     224works, which are not by their nature extensions of the covered work,
     225and which are not combined with it such as to form a larger program,
     226in or on a volume of a storage or distribution medium, is called an
     227"aggregate" if the compilation and its resulting copyright are not
     228used to limit the access or legal rights of the compilation's users
     229beyond what the individual works permit.  Inclusion of a covered work
     230in an aggregate does not cause this License to apply to the other
     231parts of the aggregate.
     232
     2336. Conveying Non-Source Forms.
     234
     235You may convey a covered work in object code form under the terms
     236of sections 4 and 5, provided that you also convey the
     237machine-readable Corresponding Source under the terms of this License,
     238in one of these ways:
     239
     240a) Convey the object code in, or embodied in, a physical product
     241(including a physical distribution medium), accompanied by the
     242Corresponding Source fixed on a durable physical medium
     243customarily used for software interchange.
     244
     245b) Convey the object code in, or embodied in, a physical product
     246(including a physical distribution medium), accompanied by a
     247written offer, valid for at least three years and valid for as
     248long as you offer spare parts or customer support for that product
     249model, to give anyone who possesses the object code either (1) a
     250copy of the Corresponding Source for all the software in the
     251product that is covered by this License, on a durable physical
     252medium customarily used for software interchange, for a price no
     253more than your reasonable cost of physically performing this
     254conveying of source, or (2) access to copy the
     255Corresponding Source from a network server at no charge.
     256
     257c) Convey individual copies of the object code with a copy of the
     258written offer to provide the Corresponding Source.  This
     259alternative is allowed only occasionally and noncommercially, and
     260only if you received the object code with such an offer, in accord
     261with subsection 6b.
     262
     263d) Convey the object code by offering access from a designated
     264place (gratis or for a charge), and offer equivalent access to the
     265Corresponding Source in the same way through the same place at no
     266further charge.  You need not require recipients to copy the
     267Corresponding Source along with the object code.  If the place to
     268copy the object code is a network server, the Corresponding Source
     269may be on a different server (operated by you or a third party)
     270that supports equivalent copying facilities, provided you maintain
     271clear directions next to the object code saying where to find the
     272Corresponding Source.  Regardless of what server hosts the
     273Corresponding Source, you remain obligated to ensure that it is
     274available for as long as needed to satisfy these requirements.
     275
     276e) Convey the object code using peer-to-peer transmission, provided
     277you inform other peers where the object code and Corresponding
     278Source of the work are being offered to the general public at no
     279charge under subsection 6d.
     280
     281A separable portion of the object code, whose source code is excluded
     282from the Corresponding Source as a System Library, need not be
     283included in conveying the object code work.
     284
     285A "User Product" is either (1) a "consumer product", which means any
     286tangible personal property which is normally used for personal, family,
     287or household purposes, or (2) anything designed or sold for incorporation
     288into a dwelling.  In determining whether a product is a consumer product,
     289doubtful cases shall be resolved in favor of coverage.  For a particular
     290product received by a particular user, "normally used" refers to a
     291typical or common use of that class of product, regardless of the status
     292of the particular user or of the way in which the particular user
     293actually uses, or expects or is expected to use, the product.  A product
     294is a consumer product regardless of whether the product has substantial
     295commercial, industrial or non-consumer uses, unless such uses represent
     296the only significant mode of use of the product.
     297
     298"Installation Information" for a User Product means any methods,
     299procedures, authorization keys, or other information required to install
     300and execute modified versions of a covered work in that User Product from
     301a modified version of its Corresponding Source.  The information must
     302suffice to ensure that the continued functioning of the modified object
     303code is in no case prevented or interfered with solely because
     304modification has been made.
     305
     306If you convey an object code work under this section in, or with, or
     307specifically for use in, a User Product, and the conveying occurs as
     308part of a transaction in which the right of possession and use of the
     309User Product is transferred to the recipient in perpetuity or for a
     310fixed term (regardless of how the transaction is characterized), the
     311Corresponding Source conveyed under this section must be accompanied
     312by the Installation Information.  But this requirement does not apply
     313if neither you nor any third party retains the ability to install
     314modified object code on the User Product (for example, the work has
     315been installed in ROM).
     316
     317The requirement to provide Installation Information does not include a
     318requirement to continue to provide support service, warranty, or updates
     319for a work that has been modified or installed by the recipient, or for
     320the User Product in which it has been modified or installed.  Access to a
     321network may be denied when the modification itself materially and
     322adversely affects the operation of the network or violates the rules and
     323protocols for communication across the network.
     324
     325Corresponding Source conveyed, and Installation Information provided,
     326in accord with this section must be in a format that is publicly
     327documented (and with an implementation available to the public in
     328source code form), and must require no special password or key for
     329unpacking, reading or copying.
     330
     3317. Additional Terms.
     332
     333"Additional permissions" are terms that supplement the terms of this
     334License by making exceptions from one or more of its conditions.
     335Additional permissions that are applicable to the entire Program shall
     336be treated as though they were included in this License, to the extent
     337that they are valid under applicable law.  If additional permissions
     338apply only to part of the Program, that part may be used separately
     339under those permissions, but the entire Program remains governed by
     340this License without regard to the additional permissions.
     341
     342When you convey a copy of a covered work, you may at your option
     343remove any additional permissions from that copy, or from any part of
     344it.  (Additional permissions may be written to require their own
     345removal in certain cases when you modify the work.)  You may place
     346additional permissions on material, added by you to a covered work,
     347for which you have or can give appropriate copyright permission.
     348
     349Notwithstanding any other provision of this License, for material you
     350add to a covered work, you may (if authorized by the copyright holders of
     351that material) supplement the terms of this License with terms:
     352
     353a) Disclaiming warranty or limiting liability differently from the
     354terms of sections 15 and 16 of this License; or
     355
     356b) Requiring preservation of specified reasonable legal notices or
     357author attributions in that material or in the Appropriate Legal
     358Notices displayed by works containing it; or
     359
     360c) Prohibiting misrepresentation of the origin of that material, or
     361requiring that modified versions of such material be marked in
     362reasonable ways as different from the original version; or
     363
     364d) Limiting the use for publicity purposes of names of licensors or
     365authors of the material; or
     366
     367e) Declining to grant rights under trademark law for use of some
     368trade names, trademarks, or service marks; or
     369
     370f) Requiring indemnification of licensors and authors of that
     371material by anyone who conveys the material (or modified versions of
     372it) with contractual assumptions of liability to the recipient, for
     373any liability that these contractual assumptions directly impose on
     374those licensors and authors.
     375
     376All other non-permissive additional terms are considered "further
     377restrictions" within the meaning of section 10.  If the Program as you
     378received it, or any part of it, contains a notice stating that it is
     379governed by this License along with a term that is a further
     380restriction, you may remove that term.  If a license document contains
     381a further restriction but permits relicensing or conveying under this
     382License, you may add to a covered work material governed by the terms
     383of that license document, provided that the further restriction does
     384not survive such relicensing or conveying.
     385
     386If you add terms to a covered work in accord with this section, you
     387must place, in the relevant source files, a statement of the
     388additional terms that apply to those files, or a notice indicating
     389where to find the applicable terms.
     390
     391Additional terms, permissive or non-permissive, may be stated in the
     392form of a separately written license, or stated as exceptions;
     393the above requirements apply either way.
     394
     3958. Termination.
     396
     397You may not propagate or modify a covered work except as expressly
     398provided under this License.  Any attempt otherwise to propagate or
     399modify it is void, and will automatically terminate your rights under
     400this License (including any patent licenses granted under the third
     401paragraph of section 11).
     402
     403However, if you cease all violation of this License, then your
     404license from a particular copyright holder is reinstated (a)
     405provisionally, unless and until the copyright holder explicitly and
     406finally terminates your license, and (b) permanently, if the copyright
     407holder fails to notify you of the violation by some reasonable means
     408prior to 60 days after the cessation.
     409
     410Moreover, your license from a particular copyright holder is
     411reinstated permanently if the copyright holder notifies you of the
     412violation by some reasonable means, this is the first time you have
     413received notice of violation of this License (for any work) from that
     414copyright holder, and you cure the violation prior to 30 days after
     415your receipt of the notice.
     416
     417Termination of your rights under this section does not terminate the
     418licenses of parties who have received copies or rights from you under
     419this License.  If your rights have been terminated and not permanently
     420reinstated, you do not qualify to receive new licenses for the same
     421material under section 10.
     422
     4239. Acceptance Not Required for Having Copies.
     424
     425You are not required to accept this License in order to receive or
     426run a copy of the Program.  Ancillary propagation of a covered work
     427occurring solely as a consequence of using peer-to-peer transmission
     428to receive a copy likewise does not require acceptance.  However,
     429nothing other than this License grants you permission to propagate or
     430modify any covered work.  These actions infringe copyright if you do
     431not accept this License.  Therefore, by modifying or propagating a
     432covered work, you indicate your acceptance of this License to do so.
     433
     43410. Automatic Licensing of Downstream Recipients.
     435
     436Each time you convey a covered work, the recipient automatically
     437receives a license from the original licensors, to run, modify and
     438propagate that work, subject to this License.  You are not responsible
     439for enforcing compliance by third parties with this License.
     440
     441An "entity transaction" is a transaction transferring control of an
     442organization, or substantially all assets of one, or subdividing an
     443organization, or merging organizations.  If propagation of a covered
     444work results from an entity transaction, each party to that
     445transaction who receives a copy of the work also receives whatever
     446licenses to the work the party's predecessor in interest had or could
     447give under the previous paragraph, plus a right to possession of the
     448Corresponding Source of the work from the predecessor in interest, if
     449the predecessor has it or can get it with reasonable efforts.
     450
     451You may not impose any further restrictions on the exercise of the
     452rights granted or affirmed under this License.  For example, you may
     453not impose a license fee, royalty, or other charge for exercise of
     454rights granted under this License, and you may not initiate litigation
     455(including a cross-claim or counterclaim in a lawsuit) alleging that
     456any patent claim is infringed by making, using, selling, offering for
     457sale, or importing the Program or any portion of it.
     458
     45911. Patents.
     460
     461A "contributor" is a copyright holder who authorizes use under this
     462License of the Program or a work on which the Program is based.  The
     463work thus licensed is called the contributor's "contributor version".
     464
     465A contributor's "essential patent claims" are all patent claims
     466owned or controlled by the contributor, whether already acquired or
     467hereafter acquired, that would be infringed by some manner, permitted
     468by this License, of making, using, or selling its contributor version,
     469but do not include claims that would be infringed only as a
     470consequence of further modification of the contributor version.  For
     471purposes of this definition, "control" includes the right to grant
     472patent sublicenses in a manner consistent with the requirements of
    195473this License.
    196474
    197   7. If, as a consequence of a court judgment or allegation of patent
    198 infringement or for any other reason (not limited to patent issues),
    199 conditions are imposed on you (whether by court order, agreement or
     475Each contributor grants you a non-exclusive, worldwide, royalty-free
     476patent license under the contributor's essential patent claims, to
     477make, use, sell, offer for sale, import and otherwise run, modify and
     478propagate the contents of its contributor version.
     479
     480In the following three paragraphs, a "patent license" is any express
     481agreement or commitment, however denominated, not to enforce a patent
     482(such as an express permission to practice a patent or covenant not to
     483sue for patent infringement).  To "grant" such a patent license to a
     484party means to make such an agreement or commitment not to enforce a
     485patent against the party.
     486
     487If you convey a covered work, knowingly relying on a patent license,
     488and the Corresponding Source of the work is not available for anyone
     489to copy, free of charge and under the terms of this License, through a
     490publicly available network server or other readily accessible means,
     491then you must either (1) cause the Corresponding Source to be so
     492available, or (2) arrange to deprive yourself of the benefit of the
     493patent license for this particular work, or (3) arrange, in a manner
     494consistent with the requirements of this License, to extend the patent
     495license to downstream recipients.  "Knowingly relying" means you have
     496actual knowledge that, but for the patent license, your conveying the
     497covered work in a country, or your recipient's use of the covered work
     498in a country, would infringe one or more identifiable patents in that
     499country that you have reason to believe are valid.
     500
     501If, pursuant to or in connection with a single transaction or
     502arrangement, you convey, or propagate by procuring conveyance of, a
     503covered work, and grant a patent license to some of the parties
     504receiving the covered work authorizing them to use, propagate, modify
     505or convey a specific copy of the covered work, then the patent license
     506you grant is automatically extended to all recipients of the covered
     507work and works based on it.
     508
     509A patent license is "discriminatory" if it does not include within
     510the scope of its coverage, prohibits the exercise of, or is
     511conditioned on the non-exercise of one or more of the rights that are
     512specifically granted under this License.  You may not convey a covered
     513work if you are a party to an arrangement with a third party that is
     514in the business of distributing software, under which you make payment
     515to the third party based on the extent of your activity of conveying
     516the work, and under which the third party grants, to any of the
     517parties who would receive the covered work from you, a discriminatory
     518patent license (a) in connection with copies of the covered work
     519conveyed by you (or copies made from those copies), or (b) primarily
     520for and in connection with specific products or compilations that
     521contain the covered work, unless you entered into that arrangement,
     522or that patent license was granted, prior to 28 March 2007.
     523
     524Nothing in this License shall be construed as excluding or limiting
     525any implied license or other defenses to infringement that may
     526otherwise be available to you under applicable patent law.
     527
     52812. No Surrender of Others' Freedom.
     529
     530If conditions are imposed on you (whether by court order, agreement or
    200531otherwise) that contradict the conditions of this License, they do not
    201 excuse you from the conditions of this License.  If you cannot
    202 distribute so as to satisfy simultaneously your obligations under this
    203 License and any other pertinent obligations, then as a consequence you
    204 may not distribute the Program at all.  For example, if a patent
    205 license would not permit royalty-free redistribution of the Program by
    206 all those who receive copies directly or indirectly through you, then
    207 the only way you could satisfy both it and this License would be to
    208 refrain entirely from distribution of the Program.
     532excuse you from the conditions of this License.  If you cannot convey a
     533covered work so as to satisfy simultaneously your obligations under this
     534License and any other pertinent obligations, then as a consequence you may
     535not convey it at all.  For example, if you agree to terms that obligate you
     536to collect a royalty for further conveying from those to whom you convey
     537the Program, the only way you could satisfy both those terms and this
     538License would be to refrain entirely from conveying the Program.
    209539
    210 If any portion of this section is held invalid or unenforceable under
    211 any particular circumstance, the balance of the section is intended to
    212 apply and the section as a whole is intended to apply in other
    213 circumstances.
     54013. Remote Network Interaction; Use with the GNU General Public License.
    214541
    215 It is not the purpose of this section to induce you to infringe any
    216 patents or other property right claims or to contest validity of any
    217 such claims; this section has the sole purpose of protecting the
    218 integrity of the free software distribution system, which is
    219 implemented by public license practices.  Many people have made
    220 generous contributions to the wide range of software distributed
    221 through that system in reliance on consistent application of that
    222 system; it is up to the author/donor to decide if he or she is willing
    223 to distribute software through any other system and a licensee cannot
    224 impose that choice.
     542Notwithstanding any other provision of this License, if you modify the
     543Program, your modified version must prominently offer all users
     544interacting with it remotely through a computer network (if your version
     545supports such interaction) an opportunity to receive the Corresponding
     546Source of your version by providing access to the Corresponding Source
     547from a network server at no charge, through some standard or customary
     548means of facilitating copying of software.  This Corresponding Source
     549shall include the Corresponding Source for any work covered by version 3
     550of the GNU General Public License that is incorporated pursuant to the
     551following paragraph.
    225552
    226 This section is intended to make thoroughly clear what is believed to
    227 be a consequence of the rest of this License.
     553Notwithstanding any other provision of this License, you have
     554permission to link or combine any covered work with a work licensed
     555under version 3 of the GNU General Public License into a single
     556combined work, and to convey the resulting work.  The terms of this
     557License will continue to apply to the part which is the covered work,
     558but the work with which it is combined will remain governed by version
     5593 of the GNU General Public License.
    228560
    229   8. If the distribution and/or use of the Program is restricted in
    230 certain countries either by patents or by copyrighted interfaces, the
    231 original copyright holder who places the Program under this License
    232 may add an explicit geographical distribution limitation excluding
    233 those countries, so that distribution is permitted only in or among
    234 countries not thus excluded.  In such case, this License incorporates
    235 the limitation as if written in the body of this License.
     56114. Revised Versions of this License.
    236562
    237   9. The Free Software Foundation may publish revised and/or new versions
    238 of the General Public License from time to time.  Such new versions will
    239 be similar in spirit to the present version, but may differ in detail to
     563The Free Software Foundation may publish revised and/or new versions of
     564the GNU Affero General Public License from time to time.  Such new versions
     565will be similar in spirit to the present version, but may differ in detail to
    240566address new problems or concerns.
    241567
    242 Each version is given a distinguishing version number.  If the Program
    243 specifies a version number of this License which applies to it and "any
    244 later version", you have the option of following the terms and conditions
    245 either of that version or of any later version published by the Free
    246 Software Foundation.  If the Program does not specify a version number of
    247 this License, you may choose any version ever published by the Free Software
    248 Foundation.
     568Each version is given a distinguishing version number.  If the
     569Program specifies that a certain numbered version of the GNU Affero General
     570Public License "or any later version" applies to it, you have the
     571option of following the terms and conditions either of that numbered
     572version or of any later version published by the Free Software
     573Foundation.  If the Program does not specify a version number of the
     574GNU Affero General Public License, you may choose any version ever published
     575by the Free Software Foundation.
    249576
    250   10. If you wish to incorporate parts of the Program into other free
    251 programs whose distribution conditions are different, write to the author
    252 to ask for permission.  For software which is copyrighted by the Free
    253 Software Foundation, write to the Free Software Foundation; we sometimes
    254 make exceptions for this.  Our decision will be guided by the two goals
    255 of preserving the free status of all derivatives of our free software and
    256 of promoting the sharing and reuse of software generally.
     577If the Program specifies that a proxy can decide which future
     578versions of the GNU Affero General Public License can be used, that proxy's
     579public statement of acceptance of a version permanently authorizes you
     580to choose that version for the Program.
    257581
    258                             NO WARRANTY
     582Later license versions may give you additional or different
     583permissions.  However, no additional obligations are imposed on any
     584author or copyright holder as a result of your choosing to follow a
     585later version.
    259586
    260   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
    261 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
    262 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
    263 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
    264 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    265 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
    266 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
    267 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
    268 REPAIR OR CORRECTION.
     58715. Disclaimer of Warranty.
    269588
    270   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
    271 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
    272 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
    273 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
    274 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
    275 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
    276 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
    277 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
    278 POSSIBILITY OF SUCH DAMAGES.
     589THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
     590APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
     591HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
     592OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
     593THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     594PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
     595IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
     596ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    279597
    280                      END OF TERMS AND CONDITIONS
     59816. Limitation of Liability.
    281599
    282             How to Apply These Terms to Your New Programs
     600IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
     601WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
     602THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
     603GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
     604USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
     605DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
     606PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
     607EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
     608SUCH DAMAGES.
    283609
    284   If you develop a new program, and you want it to be of the greatest
     61017. Interpretation of Sections 15 and 16.
     611
     612If the disclaimer of warranty and limitation of liability provided
     613above cannot be given local legal effect according to their terms,
     614reviewing courts shall apply local law that most closely approximates
     615an absolute waiver of all civil liability in connection with the
     616Program, unless a warranty or assumption of liability accompanies a
     617copy of the Program in return for a fee.
     618
     619 END OF TERMS AND CONDITIONS
     620
     621How to Apply These Terms to Your New Programs
     622
     623If you develop a new program, and you want it to be of the greatest
    285624possible use to the public, the best way to achieve this is to make it
    286625free software which everyone can redistribute and change under these terms.
    287626
    288   To do so, attach the following notices to the program.  It is safest
     627To do so, attach the following notices to the program.  It is safest
    289628to attach them to the start of each source file to most effectively
    290 convey the exclusion of warranty; and each file should have at least
     629state the exclusion of warranty; and each file should have at least
    291630the "copyright" line and a pointer to where the full notice is found.
    292631
    293     <one line to give the program's name and a brief idea of what it does.>
    294     Copyright (C) <year>  <name of author>
     632<one line to give the program's name and a brief idea of what it does.>
     633Copyright (C) <year>  <name of author>
    295634
    296     This program is free software; you can redistribute it and/or modify
    297     it under the terms of the GNU General Public License as published by
    298     the Free Software Foundation; either version 2 of the License, or
    299     (at your option) any later version.
     635This program is free software: you can redistribute it and/or modify
     636it under the terms of the GNU Affero General Public License as published by
     637the Free Software Foundation, either version 3 of the License, or
     638(at your option) any later version.
    300639
    301     This program is distributed in the hope that it will be useful,
    302     but WITHOUT ANY WARRANTY; without even the implied warranty of
    303     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    304     GNU General Public License for more details.
     640This program is distributed in the hope that it will be useful,
     641but WITHOUT ANY WARRANTY; without even the implied warranty of
     642MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     643GNU Affero General Public License for more details.
    305644
    306     You should have received a copy of the GNU General Public License along
    307     with this program; if not, write to the Free Software Foundation, Inc.,
    308     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     645You should have received a copy of the GNU Affero General Public License
     646along with this program.  If not, see <http://www.gnu.org/licenses/>.
    309647
    310648Also add information on how to contact you by electronic and paper mail.
    311649
    312 If the program is interactive, make it output a short notice like this
    313 when it starts in an interactive mode:
     650If your software can interact with users remotely through a computer
     651network, you should also make sure that it provides a way for users to
     652get its source.  For example, if your program is a web application, its
     653interface could display a "Source" link that leads users to an archive
     654of the code.  There are many ways you could offer source, and different
     655solutions will be better for different programs; see section 13 for the
     656specific requirements.
    314657
    315     Gnomovision version 69, Copyright (C) year name of author
    316     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    317     This is free software, and you are welcome to redistribute it
    318     under certain conditions; type `show c' for details.
    319 
    320 The hypothetical commands `show w' and `show c' should show the appropriate
    321 parts of the General Public License.  Of course, the commands you use may
    322 be called something other than `show w' and `show c'; they could even be
    323 mouse-clicks or menu items--whatever suits your program.
    324 
    325 You should also get your employer (if you work as a programmer) or your
    326 school, if any, to sign a "copyright disclaimer" for the program, if
    327 necessary.  Here is a sample; alter the names:
    328 
    329   Yoyodyne, Inc., hereby disclaims all copyright interest in the program
    330   `Gnomovision' (which makes passes at compilers) written by James Hacker.
    331 
    332   <signature of Ty Coon>, 1 April 1989
    333   Ty Coon, President of Vice
    334 
    335 This General Public License does not permit incorporating your program into
    336 proprietary programs.  If your program is a subroutine library, you may
    337 consider it more useful to permit linking proprietary applications with the
    338 library.  If this is what you want to do, use the GNU Lesser General
    339 Public License instead of this License.
     658You should also get your employer (if you work as a programmer) or school,
     659if any, to sign a "copyright disclaimer" for the program, if necessary.
     660For more information on this, and how to apply and follow the GNU AGPL, see
     661<http://www.gnu.org/licenses/>.
  • src/js/_enqueues/vendor/plupload/moxie.js

     
    11;var MXI_DEBUG = false;
    22/**
    33 * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill
    4  * v1.3.5
     4 * v1.5.7
    55 *
    66 * Copyright 2013, Moxiecode Systems AB
    77 * Released under GPL License.
     
    99 * License: http://www.plupload.com/license
    1010 * Contributing: http://www.plupload.com/contributing
    1111 *
    12  * Date: 2016-05-15
     12 * Date: 2017-11-03
    1313 */
     14;(function (global, factory) {
     15        var extract = function() {
     16                var ctx = {};
     17                factory.apply(ctx, arguments);
     18                return ctx.moxie;
     19        };
     20       
     21        if (typeof define === "function" && define.amd) {
     22                define("moxie", [], extract);
     23        } else if (typeof module === "object" && module.exports) {
     24                module.exports = extract();
     25        } else {
     26                global.moxie = extract();
     27        }
     28}(this || window, function() {
    1429/**
    1530 * Compiled inline version. (Library mode)
    1631 */
    1732
    18 /**
    19  * Modified for WordPress, Silverlight and Flash runtimes support was removed.
    20  * See https://core.trac.wordpress.org/ticket/41755.
    21  */
    22 
    2333/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
    2434/*globals $code */
    2535
     
    110120 * Contributing: http://www.plupload.com/contributing
    111121 */
    112122
     123/**
     124@class moxie/core/utils/Basic
     125@public
     126@static
     127*/
     128
    113129define('moxie/core/utils/Basic', [], function() {
    114130        /**
    115131        Gets the true type of the built-in object (better version of typeof).
     
    116132        @author Angus Croll (http://javascriptweblog.wordpress.com/)
    117133
    118134        @method typeOf
    119         @for Utils
    120135        @static
    121136        @param {Object} o Object to check.
    122137        @return {String} Object [[Class]]
    123138        */
    124         var typeOf = function(o) {
     139        function typeOf(o) {
    125140                var undef;
    126141
    127142                if (o === undef) {
     
    134149
    135150                // the snippet below is awesome, however it fails to detect null, undefined and arguments types in IE lte 8
    136151                return ({}).toString.call(o).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
    137         };
    138                
     152        }
     153
    139154        /**
    140         Extends the specified object with another object.
     155        Extends the specified object with another object(s).
    141156
    142157        @method extend
    143158        @static
     
    145160        @param {Object} [obj]* Multiple objects to extend with.
    146161        @return {Object} Same as target, the extended object.
    147162        */
    148         var extend = function(target) {
     163        function extend() {
     164                return merge(false, false, arguments);
     165        }
     166
     167
     168        /**
     169        Extends the specified object with another object(s), but only if the property exists in the target.
     170
     171        @method extendIf
     172        @static
     173        @param {Object} target Object to extend.
     174        @param {Object} [obj]* Multiple objects to extend with.
     175        @return {Object} Same as target, the extended object.
     176        */
     177        function extendIf() {
     178                return merge(true, false, arguments);
     179        }
     180
     181
     182        function extendImmutable() {
     183                return merge(false, true, arguments);
     184        }
     185
     186
     187        function extendImmutableIf() {
     188                return merge(true, true, arguments);
     189        }
     190
     191
     192        function clone(value) {
     193                switch (typeOf(value)) {
     194                        case 'array':
     195                                return merge(false, true, [[], value]);
     196
     197                        case 'object':
     198                                return merge(false, true, [{}, value]);
     199
     200                        default:
     201                                return value;
     202                }
     203        }
     204
     205
     206        function shallowCopy(obj) {
     207                switch (typeOf(obj)) {
     208                        case 'array':
     209                                return Array.prototype.slice.call(obj);
     210
     211                        case 'object':
     212                                return extend({}, obj);
     213                }
     214                return obj;
     215        }
     216
     217
     218        function merge(strict, immutable, args) {
    149219                var undef;
     220                var target = args[0];
    150221
    151                 each(arguments, function(arg, i) {
     222                each(args, function(arg, i) {
    152223                        if (i > 0) {
    153224                                each(arg, function(value, key) {
    154                                         if (value !== undef) {
    155                                                 if (typeOf(target[key]) === typeOf(value) && !!~inArray(typeOf(value), ['array', 'object'])) {
    156                                                         extend(target[key], value);
    157                                                 } else {
    158                                                         target[key] = value;
    159                                                 }
     225                                        var isComplex = inArray(typeOf(value), ['array', 'object']) !== -1;
     226
     227                                        if (value === undef || strict && target[key] === undef) {
     228                                                return true;
    160229                                        }
     230
     231                                        if (isComplex && immutable) {
     232                                                value = shallowCopy(value);
     233                                        }
     234
     235                                        if (typeOf(target[key]) === typeOf(value) && isComplex) {
     236                                                merge(strict, immutable, [target[key], value]);
     237                                        } else {
     238                                                target[key] = value;
     239                                        }
    161240                                });
    162241                        }
    163242                });
     243
    164244                return target;
    165         };
    166                
     245        }
     246
     247
    167248        /**
     249        A way to inherit one `class` from another in a consisstent way (more or less)
     250
     251        @method inherit
     252        @static
     253        @since >1.4.1
     254        @param {Function} child
     255        @param {Function} parent
     256        @return {Function} Prepared constructor
     257        */
     258        function inherit(child, parent) {
     259                // copy over all parent properties
     260                for (var key in parent) {
     261                        if ({}.hasOwnProperty.call(parent, key)) {
     262                                child[key] = parent[key];
     263                        }
     264                }
     265
     266                // give child `class` a place to define its own methods
     267                function ctor() {
     268                        this.constructor = child;
     269
     270                        if (MXI_DEBUG) {
     271                                var getCtorName = function(fn) {
     272                                        var m = fn.toString().match(/^function\s([^\(\s]+)/);
     273                                        return m ? m[1] : false;
     274                                };
     275
     276                                this.ctorName = getCtorName(child);
     277                        }
     278                }
     279                ctor.prototype = parent.prototype;
     280                child.prototype = new ctor();
     281
     282                // keep a way to reference parent methods
     283                child.parent = parent.prototype;
     284                return child;
     285        }
     286
     287
     288        /**
    168289        Executes the callback function for each item in array/object. If you return false in the
    169290        callback it will break the loop.
    170291
     
    173294        @param {Object} obj Object to iterate.
    174295        @param {function} callback Callback function to execute for each item.
    175296        */
    176         var each = function(obj, callback) {
     297        function each(obj, callback) {
    177298                var length, key, i, undef;
    178299
    179300                if (obj) {
    180                         if (typeOf(obj.length) === 'number') { // it might be Array, FileList or even arguments object
    181                                 // Loop array items
    182                                 for (i = 0, length = obj.length; i < length; i++) {
    183                                         if (callback(obj[i], i) === false) {
    184                                                 return;
    185                                         }
    186                                 }
    187                         } else if (typeOf(obj) === 'object') {
     301                        try {
     302                                length = obj.length;
     303                        } catch(ex) {
     304                                length = undef;
     305                        }
     306
     307                        if (length === undef || typeof(length) !== 'number') {
    188308                                // Loop object items
    189309                                for (key in obj) {
    190310                                        if (obj.hasOwnProperty(key)) {
     
    193313                                                }
    194314                                        }
    195315                                }
     316                        } else {
     317                                // Loop array items
     318                                for (i = 0; i < length; i++) {
     319                                        if (callback(obj[i], i) === false) {
     320                                                return;
     321                                        }
     322                                }
    196323                        }
    197324                }
    198         };
     325        }
    199326
    200327        /**
    201328        Checks if object is empty.
    202        
     329
    203330        @method isEmptyObj
    204331        @static
    205332        @param {Object} o Object to check.
    206333        @return {Boolean}
    207334        */
    208         var isEmptyObj = function(obj) {
     335        function isEmptyObj(obj) {
    209336                var prop;
    210337
    211338                if (!obj || typeOf(obj) !== 'object') {
     
    217344                }
    218345
    219346                return true;
    220         };
     347        }
    221348
    222349        /**
    223350        Recieve an array of functions (usually async) to call in sequence, each  function
     
    231358        @param {Array} queue Array of functions to call in sequence
    232359        @param {Function} cb Main callback that is called in the end, or in case of error
    233360        */
    234         var inSeries = function(queue, cb) {
     361        function inSeries(queue, cb) {
    235362                var i = 0, length = queue.length;
    236363
    237364                if (typeOf(cb) !== 'function') {
     
    251378                        }
    252379                }
    253380                callNext(i);
    254         };
     381        }
    255382
    256383
    257384        /**
    258385        Recieve an array of functions (usually async) to call in parallel, each  function
    259         receives a callback as first argument that it should call, when it completes. After 
     386        receives a callback as first argument that it should call, when it completes. After
    260387        everything is complete, main callback is called. Passing truthy value to the
    261388        callback as a first argument will interrupt the process and invoke main callback
    262389        immediately.
     
    264391        @method inParallel
    265392        @static
    266393        @param {Array} queue Array of functions to call in sequence
    267         @param {Function} cb Main callback that is called in the end, or in case of error
     394        @param {Function} cb Main callback that is called in the end, or in case of erro
    268395        */
    269         var inParallel = function(queue, cb) {
     396        function inParallel(queue, cb) {
    270397                var count = 0, num = queue.length, cbArgs = new Array(num);
    271398
    272399                each(queue, function(fn, i) {
     
    274401                                if (error) {
    275402                                        return cb(error);
    276403                                }
    277                                
     404
    278405                                var args = [].slice.call(arguments);
    279406                                args.shift(); // strip error - undefined or not
    280407
     
    284411                                if (count === num) {
    285412                                        cbArgs.unshift(null);
    286413                                        cb.apply(this, cbArgs);
    287                                 } 
     414                                }
    288415                        });
    289416                });
    290         };
    291        
    292        
     417        }
     418
     419
    293420        /**
    294421        Find an element in array and return it's index if present, otherwise return -1.
    295        
     422
    296423        @method inArray
    297424        @static
    298425        @param {Mixed} needle Element to find
     
    299426        @param {Array} array
    300427        @return {Int} Index of the element, or -1 if not found
    301428        */
    302         var inArray = function(needle, array) {
     429        function inArray(needle, array) {
    303430                if (array) {
    304431                        if (Array.prototype.indexOf) {
    305432                                return Array.prototype.indexOf.call(array, needle);
    306433                        }
    307                
     434
    308435                        for (var i = 0, length = array.length; i < length; i++) {
    309436                                if (array[i] === needle) {
    310437                                        return i;
     
    312439                        }
    313440                }
    314441                return -1;
    315         };
     442        }
    316443
    317444
    318445        /**
     
    324451        @param {Array} array
    325452        @return {Array|Boolean}
    326453        */
    327         var arrayDiff = function(needles, array) {
     454        function arrayDiff(needles, array) {
    328455                var diff = [];
    329456
    330457                if (typeOf(needles) !== 'array') {
     
    338465                for (var i in needles) {
    339466                        if (inArray(needles[i], array) === -1) {
    340467                                diff.push(needles[i]);
    341                         }       
     468                        }
    342469                }
    343470                return diff.length ? diff : false;
    344         };
     471        }
    345472
    346473
    347474        /**
     
    353480        @param {Array} array2
    354481        @return {Array} Intersection of two arrays or null if there is none
    355482        */
    356         var arrayIntersect = function(array1, array2) {
     483        function arrayIntersect(array1, array2) {
    357484                var result = [];
    358485                each(array1, function(item) {
    359486                        if (inArray(item, array2) !== -1) {
     
    361488                        }
    362489                });
    363490                return result.length ? result : null;
    364         };
    365        
    366        
     491        }
     492
     493
    367494        /**
    368495        Forces anything into an array.
    369        
     496
    370497        @method toArray
    371498        @static
    372499        @param {Object} obj Object with length field.
    373500        @return {Array} Array object containing all items.
    374501        */
    375         var toArray = function(obj) {
     502        function toArray(obj) {
    376503                var i, arr = [];
    377504
    378505                for (i = 0; i < obj.length; i++) {
     
    380507                }
    381508
    382509                return arr;
    383         };
    384        
    385                        
     510        }
     511
     512
    386513        /**
    387514        Generates an unique ID. The only way a user would be able to get the same ID is if the two persons
    388         at the same exact millisecond manage to get the same 5 random numbers between 0-65535; it also uses 
    389         a counter so each ID is guaranteed to be unique for the given page. It is more probable for the earth 
     515        at the same exact millisecond manage to get the same 5 random numbers between 0-65535; it also uses
     516        a counter so each ID is guaranteed to be unique for the given page. It is more probable for the earth
    390517        to be hit with an asteroid.
    391        
     518
    392519        @method guid
    393520        @static
    394521        @param {String} prefix to prepend (by default 'o' will be prepended).
     
    397524        */
    398525        var guid = (function() {
    399526                var counter = 0;
    400                
     527
    401528                return function(prefix) {
    402529                        var guid = new Date().getTime().toString(32), i;
    403530
     
    404531                        for (i = 0; i < 5; i++) {
    405532                                guid += Math.floor(Math.random() * 65535).toString(32);
    406533                        }
    407                        
     534
    408535                        return (prefix || 'o_') + guid + (counter++).toString(32);
    409536                };
    410537        }());
    411        
    412538
     539
    413540        /**
    414541        Trims white spaces around the string
    415        
     542
    416543        @method trim
    417544        @static
    418545        @param {String} str
    419546        @return {String}
    420547        */
    421         var trim = function(str) {
     548        function trim(str) {
    422549                if (!str) {
    423550                        return str;
    424551                }
    425552                return String.prototype.trim ? String.prototype.trim.call(str) : str.toString().replace(/^\s*/, '').replace(/\s*$/, '');
    426         };
     553        }
    427554
    428555
    429556        /**
    430557        Parses the specified size string into a byte value. For example 10kb becomes 10240.
    431        
     558
    432559        @method parseSizeStr
    433560        @static
    434561        @param {String/Number} size String to parse or number to just pass through.
    435562        @return {Number} Size in bytes.
    436563        */
    437         var parseSizeStr = function(size) {
     564        function parseSizeStr(size) {
    438565                if (typeof(size) !== 'string') {
    439566                        return size;
    440567                }
    441                
     568
    442569                var muls = {
    443570                                t: 1099511627776,
    444571                                g: 1073741824,
     
    447574                        },
    448575                        mul;
    449576
    450 
    451577                size = /^([0-9\.]+)([tmgk]?)$/.exec(size.toLowerCase().replace(/[^0-9\.tmkg]/g, ''));
    452578                mul = size[2];
    453579                size = +size[1];
    454                
     580
    455581                if (muls.hasOwnProperty(mul)) {
    456582                        size *= muls[mul];
    457583                }
    458584                return Math.floor(size);
    459         };
     585        }
    460586
    461587
    462588        /**
     
    465591         * @param {String} str String with tokens
    466592         * @return {String} String with replaced tokens
    467593         */
    468         var sprintf = function(str) {
     594        function sprintf(str) {
    469595                var args = [].slice.call(arguments, 1);
    470596
    471                 return str.replace(/%[a-z]/g, function() {
     597                return str.replace(/%([a-z])/g, function($0, $1) {
    472598                        var value = args.shift();
    473                         return typeOf(value) !== 'undefined' ? value : '';
     599
     600                        switch ($1) {
     601                                case 's':
     602                                        return value + '';
     603
     604                                case 'd':
     605                                        return parseInt(value, 10);
     606
     607                                case 'f':
     608                                        return parseFloat(value);
     609
     610                                case 'c':
     611                                        return '';
     612
     613                                default:
     614                                        return value;
     615                        }
    474616                });
    475         };
    476        
     617        }
    477618
     619
     620
     621        function delay(cb, timeout) {
     622                var self = this;
     623                setTimeout(function() {
     624                        cb.call(self);
     625                }, timeout || 1);
     626        }
     627
     628
    478629        return {
    479630                guid: guid,
    480631                typeOf: typeOf,
    481632                extend: extend,
     633                extendIf: extendIf,
     634                extendImmutable: extendImmutable,
     635                extendImmutableIf: extendImmutableIf,
     636                clone: clone,
     637                inherit: inherit,
    482638                each: each,
    483639                isEmptyObj: isEmptyObj,
    484640                inSeries: inSeries,
     
    489645                toArray: toArray,
    490646                trim: trim,
    491647                sprintf: sprintf,
    492                 parseSizeStr: parseSizeStr
     648                parseSizeStr: parseSizeStr,
     649                delay: delay
    493650        };
    494651});
    495652
     653// Included from: src/javascript/core/utils/Encode.js
     654
     655/**
     656 * Encode.js
     657 *
     658 * Copyright 2013, Moxiecode Systems AB
     659 * Released under GPL License.
     660 *
     661 * License: http://www.plupload.com/license
     662 * Contributing: http://www.plupload.com/contributing
     663 */
     664
     665/**
     666@class moxie/core/utils/Encode
     667@public
     668@static
     669*/
     670
     671define('moxie/core/utils/Encode', [], function() {
     672
     673        /**
     674        Encode string with UTF-8
     675
     676        @method utf8_encode
     677        @static
     678        @param {String} str String to encode
     679        @return {String} UTF-8 encoded string
     680        */
     681        var utf8_encode = function(str) {
     682                return unescape(encodeURIComponent(str));
     683        };
     684       
     685        /**
     686        Decode UTF-8 encoded string
     687
     688        @method utf8_decode
     689        @static
     690        @param {String} str String to decode
     691        @return {String} Decoded string
     692        */
     693        var utf8_decode = function(str_data) {
     694                return decodeURIComponent(escape(str_data));
     695        };
     696       
     697        /**
     698        Decode Base64 encoded string (uses browser's default method if available),
     699        from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_decode.js
     700
     701        @method atob
     702        @static
     703        @param {String} data String to decode
     704        @return {String} Decoded string
     705        */
     706        var atob = function(data, utf8) {
     707                if (typeof(window.atob) === 'function') {
     708                        return utf8 ? utf8_decode(window.atob(data)) : window.atob(data);
     709                }
     710
     711                // http://kevin.vanzonneveld.net
     712                // +   original by: Tyler Akins (http://rumkin.com)
     713                // +   improved by: Thunder.m
     714                // +      input by: Aman Gupta
     715                // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
     716                // +   bugfixed by: Onno Marsman
     717                // +   bugfixed by: Pellentesque Malesuada
     718                // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
     719                // +      input by: Brett Zamir (http://brett-zamir.me)
     720                // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
     721                // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
     722                // *     returns 1: 'Kevin van Zonneveld'
     723                // mozilla has this native
     724                // - but breaks in 2.0.0.12!
     725                //if (typeof this.window.atob == 'function') {
     726                //    return atob(data);
     727                //}
     728                var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
     729                var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
     730                        ac = 0,
     731                        dec = "",
     732                        tmp_arr = [];
     733
     734                if (!data) {
     735                        return data;
     736                }
     737
     738                data += '';
     739
     740                do { // unpack four hexets into three octets using index points in b64
     741                        h1 = b64.indexOf(data.charAt(i++));
     742                        h2 = b64.indexOf(data.charAt(i++));
     743                        h3 = b64.indexOf(data.charAt(i++));
     744                        h4 = b64.indexOf(data.charAt(i++));
     745
     746                        bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
     747
     748                        o1 = bits >> 16 & 0xff;
     749                        o2 = bits >> 8 & 0xff;
     750                        o3 = bits & 0xff;
     751
     752                        if (h3 == 64) {
     753                                tmp_arr[ac++] = String.fromCharCode(o1);
     754                        } else if (h4 == 64) {
     755                                tmp_arr[ac++] = String.fromCharCode(o1, o2);
     756                        } else {
     757                                tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
     758                        }
     759                } while (i < data.length);
     760
     761                dec = tmp_arr.join('');
     762
     763                return utf8 ? utf8_decode(dec) : dec;
     764        };
     765       
     766        /**
     767        Base64 encode string (uses browser's default method if available),
     768        from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_encode.js
     769
     770        @method btoa
     771        @static
     772        @param {String} data String to encode
     773        @return {String} Base64 encoded string
     774        */
     775        var btoa = function(data, utf8) {
     776                if (utf8) {
     777                        data = utf8_encode(data);
     778                }
     779
     780                if (typeof(window.btoa) === 'function') {
     781                        return window.btoa(data);
     782                }
     783
     784                // http://kevin.vanzonneveld.net
     785                // +   original by: Tyler Akins (http://rumkin.com)
     786                // +   improved by: Bayron Guevara
     787                // +   improved by: Thunder.m
     788                // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
     789                // +   bugfixed by: Pellentesque Malesuada
     790                // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
     791                // +   improved by: Rafał Kukawski (http://kukawski.pl)
     792                // *     example 1: base64_encode('Kevin van Zonneveld');
     793                // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
     794                // mozilla has this native
     795                // - but breaks in 2.0.0.12!
     796                var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
     797                var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
     798                        ac = 0,
     799                        enc = "",
     800                        tmp_arr = [];
     801
     802                if (!data) {
     803                        return data;
     804                }
     805
     806                do { // pack three octets into four hexets
     807                        o1 = data.charCodeAt(i++);
     808                        o2 = data.charCodeAt(i++);
     809                        o3 = data.charCodeAt(i++);
     810
     811                        bits = o1 << 16 | o2 << 8 | o3;
     812
     813                        h1 = bits >> 18 & 0x3f;
     814                        h2 = bits >> 12 & 0x3f;
     815                        h3 = bits >> 6 & 0x3f;
     816                        h4 = bits & 0x3f;
     817
     818                        // use hexets to index into b64, and append result to encoded string
     819                        tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
     820                } while (i < data.length);
     821
     822                enc = tmp_arr.join('');
     823
     824                var r = data.length % 3;
     825
     826                return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
     827        };
     828
     829
     830        return {
     831                utf8_encode: utf8_encode,
     832                utf8_decode: utf8_decode,
     833                atob: atob,
     834                btoa: btoa
     835        };
     836});
     837
    496838// Included from: src/javascript/core/utils/Env.js
    497839
    498840/**
     
    505847 * Contributing: http://www.plupload.com/contributing
    506848 */
    507849
     850/**
     851@class moxie/core/utils/Env
     852@public
     853@static
     854*/
     855
    508856define("moxie/core/utils/Env", [
    509857        "moxie/core/utils/Basic"
    510858], function(Basic) {
    511        
     859
    512860        /**
    513861         * UAParser.js v0.7.7
    514862         * Lightweight JavaScript-based User-Agent string parser
     
    7111059            var regexes = {
    7121060
    7131061                browser : [[
    714                
     1062
    7151063                    // Presto based
    7161064                    /(opera\smini)\/([\w\.-]+)/i,                                       // Opera Mini
    7171065                    /(opera\s[mobiletab]+).+version\/([\w\.-]+)/i,                      // Opera Mobi/Tablet
     
    10461394
    10471395        var can = (function() {
    10481396                var caps = {
    1049                                 define_property: (function() {
    1050                                         /* // currently too much extra code required, not exactly worth it
    1051                                         try { // as of IE8, getters/setters are supported only on DOM elements
    1052                                                 var obj = {};
    1053                                                 if (Object.defineProperty) {
    1054                                                         Object.defineProperty(obj, 'prop', {
    1055                                                                 enumerable: true,
    1056                                                                 configurable: true
    1057                                                         });
    1058                                                         return true;
    1059                                                 }
    1060                                         } catch(ex) {}
     1397                        access_global_ns: function () {
     1398                                return !!window.moxie;
     1399                        },
    10611400
    1062                                         if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) {
     1401                        define_property: (function() {
     1402                                /* // currently too much extra code required, not exactly worth it
     1403                                try { // as of IE8, getters/setters are supported only on DOM elements
     1404                                        var obj = {};
     1405                                        if (Object.defineProperty) {
     1406                                                Object.defineProperty(obj, 'prop', {
     1407                                                        enumerable: true,
     1408                                                        configurable: true
     1409                                                });
    10631410                                                return true;
    1064                                         }*/
    1065                                         return false;
    1066                                 }()),
     1411                                        }
     1412                                } catch(ex) {}
    10671413
    1068                                 create_canvas: (function() {
    1069                                         // On the S60 and BB Storm, getContext exists, but always returns undefined
    1070                                         // so we actually have to call getContext() to verify
    1071                                         // github.com/Modernizr/Modernizr/issues/issue/97/
    1072                                         var el = document.createElement('canvas');
    1073                                         return !!(el.getContext && el.getContext('2d'));
    1074                                 }()),
     1414                                if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) {
     1415                                        return true;
     1416                                }*/
     1417                                return false;
     1418                        }()),
    10751419
    1076                                 return_response_type: function(responseType) {
    1077                                         try {
    1078                                                 if (Basic.inArray(responseType, ['', 'text', 'document']) !== -1) {
     1420                        create_canvas: function() {
     1421                                // On the S60 and BB Storm, getContext exists, but always returns undefined
     1422                                // so we actually have to call getContext() to verify
     1423                                // github.com/Modernizr/Modernizr/issues/issue/97/
     1424                                var el = document.createElement('canvas');
     1425                                var isSupported = !!(el.getContext && el.getContext('2d'));
     1426                                caps.create_canvas = isSupported;
     1427                                return isSupported;
     1428                        },
     1429
     1430                        return_response_type: function(responseType) {
     1431                                try {
     1432                                        if (Basic.inArray(responseType, ['', 'text', 'document']) !== -1) {
     1433                                                return true;
     1434                                        } else if (window.XMLHttpRequest) {
     1435                                                var xhr = new XMLHttpRequest();
     1436                                                xhr.open('get', '/'); // otherwise Gecko throws an exception
     1437                                                if ('responseType' in xhr) {
     1438                                                        xhr.responseType = responseType;
     1439                                                        // as of 23.0.1271.64, Chrome switched from throwing exception to merely logging it to the console (why? o why?)
     1440                                                        if (xhr.responseType !== responseType) {
     1441                                                                return false;
     1442                                                        }
    10791443                                                        return true;
    1080                                                 } else if (window.XMLHttpRequest) {
    1081                                                         var xhr = new XMLHttpRequest();
    1082                                                         xhr.open('get', '/'); // otherwise Gecko throws an exception
    1083                                                         if ('responseType' in xhr) {
    1084                                                                 xhr.responseType = responseType;
    1085                                                                 // as of 23.0.1271.64, Chrome switched from throwing exception to merely logging it to the console (why? o why?)
    1086                                                                 if (xhr.responseType !== responseType) {
    1087                                                                         return false;
    1088                                                                 }
    1089                                                                 return true;
    1090                                                         }
    10911444                                                }
    1092                                         } catch (ex) {}
    1093                                         return false;
    1094                                 },
     1445                                        }
     1446                                } catch (ex) {}
     1447                                return false;
     1448                        },
    10951449
    1096                                 // ideas for this heavily come from Modernizr (http://modernizr.com/)
    1097                                 use_data_uri: (function() {
    1098                                         var du = new Image();
     1450                        use_blob_uri: function() {
     1451                                var URL = window.URL;
     1452                                caps.use_blob_uri = (URL &&
     1453                                        'createObjectURL' in URL &&
     1454                                        'revokeObjectURL' in URL &&
     1455                                        (Env.browser !== 'IE' || Env.verComp(Env.version, '11.0.46', '>=')) // IE supports createObjectURL, but not fully, for example it fails to use it as a src for the image
     1456                                );
     1457                                return caps.use_blob_uri;
     1458                        },
    10991459
    1100                                         du.onload = function() {
    1101                                                 caps.use_data_uri = (du.width === 1 && du.height === 1);
    1102                                         };
    1103                                        
    1104                                         setTimeout(function() {
    1105                                                 du.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
    1106                                         }, 1);
    1107                                         return false;
    1108                                 }()),
     1460                        // ideas for this heavily come from Modernizr (http://modernizr.com/)
     1461                        use_data_uri: (function() {
     1462                                var du = new Image();
    11091463
    1110                                 use_data_uri_over32kb: function() { // IE8
    1111                                         return caps.use_data_uri && (Env.browser !== 'IE' || Env.version >= 9);
    1112                                 },
     1464                                du.onload = function() {
     1465                                        caps.use_data_uri = (du.width === 1 && du.height === 1);
     1466                                };
    11131467
    1114                                 use_data_uri_of: function(bytes) {
    1115                                         return (caps.use_data_uri && bytes < 33000 || caps.use_data_uri_over32kb());
    1116                                 },
     1468                                setTimeout(function() {
     1469                                        du.src = "data:image/gif;base64,R0lGODlhAQABAIAAAP8AAAAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
     1470                                }, 1);
     1471                                return false;
     1472                        }()),
    11171473
    1118                                 use_fileinput: function() {
    1119                                         if (navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/)) {
    1120                                                 return false;
    1121                                         }
     1474                        use_data_uri_over32kb: function() { // IE8
     1475                                return caps.use_data_uri && (Env.browser !== 'IE' || Env.version >= 9);
     1476                        },
    11221477
    1123                                         var el = document.createElement('input');
    1124                                         el.setAttribute('type', 'file');
    1125                                         return !el.disabled;
     1478                        use_data_uri_of: function(bytes) {
     1479                                return (caps.use_data_uri && bytes < 33000 || caps.use_data_uri_over32kb());
     1480                        },
     1481
     1482                        use_fileinput: function() {
     1483                                if (navigator.userAgent.match(/(Android (1.0|1.1|1.5|1.6|2.0|2.1))|(Windows Phone (OS 7|8.0))|(XBLWP)|(ZuneWP)|(w(eb)?OSBrowser)|(webOS)|(Kindle\/(1.0|2.0|2.5|3.0))/)) {
     1484                                        return false;
    11261485                                }
    1127                         };
    11281486
     1487                                var el = document.createElement('input');
     1488                                el.setAttribute('type', 'file');
     1489                                return caps.use_fileinput = !el.disabled;
     1490                        },
     1491
     1492                        use_webgl: function() {
     1493                                var canvas = document.createElement('canvas');
     1494                                var gl = null, isSupported;
     1495                                try {
     1496                                        gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
     1497                                }
     1498                                catch(e) {}
     1499
     1500                                if (!gl) { // it seems that sometimes it doesn't throw exception, but still fails to get context
     1501                                        gl = null;
     1502                                }
     1503
     1504                                isSupported = !!gl;
     1505                                caps.use_webgl = isSupported; // save result of our check
     1506                                canvas = undefined;
     1507                                return isSupported;
     1508                        }
     1509                };
     1510
    11291511                return function(cap) {
    11301512                        var args = [].slice.call(arguments);
    11311513                        args.shift(); // shift of cap
     
    11411523                can: can,
    11421524
    11431525                uaParser: UAParser,
    1144                
     1526
    11451527                browser: uaResult.browser.name,
    11461528                version: uaResult.browser.version,
    11471529                os: uaResult.os.name, // everybody intuitively types it in a lowercase for some reason
     
    11491531
    11501532                verComp: version_compare,
    11511533
     1534                swf_url: "../flash/Moxie.swf",
     1535                xap_url: "../silverlight/Moxie.xap",
    11521536                global_event_dispatcher: "moxie.core.EventTarget.instance.dispatchEvent"
    11531537        };
    11541538
     
    11631547                };
    11641548
    11651549                Env.log = function() {
    1166                        
     1550
    11671551                        function logObj(data) {
    11681552                                // TODO: this should recursively print out the object in a pretty way
    11691553                                console.appendChild(document.createTextNode(data + "\n"));
    11701554                        }
    11711555
    1172                         var data = arguments[0];
    1173 
    1174                         if (Basic.typeOf(data) === 'string') {
    1175                                 data = Basic.sprintf.apply(this, arguments);
    1176                         }
    1177 
    1178                         if (window && window.console && window.console.log) {
    1179                                 window.console.log(data);
     1556                        // if debugger present, IE8 might have window.console.log method, but not be able to apply on it (why...)
     1557                        if (window && window.console && window.console.log && window.console.log.apply) {
     1558                                window.console.log.apply(window.console, arguments);
    11801559                        } else if (document) {
    11811560                                var console = document.getElementById('moxie-console');
    11821561                                if (!console) {
     
    11861565                                        document.body.appendChild(console);
    11871566                                }
    11881567
    1189                                 if (Basic.inArray(Basic.typeOf(data), ['object', 'array']) !== -1) {
     1568                                var data = arguments[0];
     1569                                if (Basic.typeOf(data) === 'string') {
     1570                                        data = Basic.sprintf.apply(this, arguments);
     1571                                } else if (Basic.inArray(Basic.typeOf(data), ['object', 'array']) !== -1) {
    11901572                                        logObj(data);
    1191                                 } else {
    1192                                         console.appendChild(document.createTextNode(data + "\n"));
     1573                                        return;
    11931574                                }
     1575
     1576                                console.appendChild(document.createTextNode(data + "\n"));
    11941577                        }
    11951578                };
    11961579        }
     
    11981581        return Env;
    11991582});
    12001583
    1201 // Included from: src/javascript/core/I18n.js
     1584// Included from: src/javascript/core/Exceptions.js
    12021585
    12031586/**
    1204  * I18n.js
     1587 * Exceptions.js
    12051588 *
    12061589 * Copyright 2013, Moxiecode Systems AB
    12071590 * Released under GPL License.
     
    12101593 * Contributing: http://www.plupload.com/contributing
    12111594 */
    12121595
    1213 define("moxie/core/I18n", [
    1214         "moxie/core/utils/Basic"
     1596define('moxie/core/Exceptions', [
     1597        'moxie/core/utils/Basic'
    12151598], function(Basic) {
    1216         var i18n = {};
     1599       
     1600        function _findKey(obj, value) {
     1601                var key;
     1602                for (key in obj) {
     1603                        if (obj[key] === value) {
     1604                                return key;
     1605                        }
     1606                }
     1607                return null;
     1608        }
    12171609
     1610        /**
     1611        @class moxie/core/Exception
     1612        */
    12181613        return {
    1219                 /**
    1220                  * Extends the language pack object with new items.
    1221                  *
    1222                  * @param {Object} pack Language pack items to add.
    1223                  * @return {Object} Extended language pack object.
    1224                  */
    1225                 addI18n: function(pack) {
    1226                         return Basic.extend(i18n, pack);
    1227                 },
     1614                RuntimeError: (function() {
     1615                        var namecodes = {
     1616                                NOT_INIT_ERR: 1,
     1617                                EXCEPTION_ERR: 3,
     1618                                NOT_SUPPORTED_ERR: 9,
     1619                                JS_ERR: 4
     1620                        };
    12281621
    1229                 /**
    1230                  * Translates the specified string by checking for the english string in the language pack lookup.
    1231                  *
    1232                  * @param {String} str String to look for.
    1233                  * @return {String} Translated string or the input string if it wasn't found.
    1234                  */
    1235                 translate: function(str) {
    1236                         return i18n[str] || str;
    1237                 },
    1238 
    1239                 /**
    1240                  * Shortcut for translate function
    1241                  *
    1242                  * @param {String} str String to look for.
    1243                  * @return {String} Translated string or the input string if it wasn't found.
    1244                  */
    1245                 _: function(str) {
    1246                         return this.translate(str);
    1247                 },
    1248 
    1249                 /**
    1250                  * Pseudo sprintf implementation - simple way to replace tokens with specified values.
    1251                  *
    1252                  * @param {String} str String with tokens
    1253                  * @return {String} String with replaced tokens
    1254                  */
    1255                 sprintf: function(str) {
    1256                         var args = [].slice.call(arguments, 1);
    1257 
    1258                         return str.replace(/%[a-z]/g, function() {
    1259                                 var value = args.shift();
    1260                                 return Basic.typeOf(value) !== 'undefined' ? value : '';
     1622                        function RuntimeError(code, message) {
     1623                                this.code = code;
     1624                                this.name = _findKey(namecodes, code);
     1625                                this.message = this.name + (message || ": RuntimeError " + this.code);
     1626                        }
     1627                       
     1628                        Basic.extend(RuntimeError, namecodes);
     1629                        RuntimeError.prototype = Error.prototype;
     1630                        return RuntimeError;
     1631                }()),
     1632               
     1633                OperationNotAllowedException: (function() {
     1634                       
     1635                        function OperationNotAllowedException(code) {
     1636                                this.code = code;
     1637                                this.name = 'OperationNotAllowedException';
     1638                        }
     1639                       
     1640                        Basic.extend(OperationNotAllowedException, {
     1641                                NOT_ALLOWED_ERR: 1
    12611642                        });
    1262                 }
    1263         };
    1264 });
     1643                       
     1644                        OperationNotAllowedException.prototype = Error.prototype;
     1645                       
     1646                        return OperationNotAllowedException;
     1647                }()),
    12651648
    1266 // Included from: src/javascript/core/utils/Mime.js
     1649                ImageError: (function() {
     1650                        var namecodes = {
     1651                                WRONG_FORMAT: 1,
     1652                                MAX_RESOLUTION_ERR: 2,
     1653                                INVALID_META_ERR: 3
     1654                        };
    12671655
    1268 /**
    1269  * Mime.js
    1270  *
    1271  * Copyright 2013, Moxiecode Systems AB
    1272  * Released under GPL License.
    1273  *
    1274  * License: http://www.plupload.com/license
    1275  * Contributing: http://www.plupload.com/contributing
    1276  */
    1277 
    1278 define("moxie/core/utils/Mime", [
    1279         "moxie/core/utils/Basic",
    1280         "moxie/core/I18n"
    1281 ], function(Basic, I18n) {
    1282        
    1283         var mimeData = "" +
    1284                 "application/msword,doc dot," +
    1285                 "application/pdf,pdf," +
    1286                 "application/pgp-signature,pgp," +
    1287                 "application/postscript,ps ai eps," +
    1288                 "application/rtf,rtf," +
    1289                 "application/vnd.ms-excel,xls xlb," +
    1290                 "application/vnd.ms-powerpoint,ppt pps pot," +
    1291                 "application/zip,zip," +
    1292                 "application/x-shockwave-flash,swf swfl," +
    1293                 "application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx," +
    1294                 "application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx," +
    1295                 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx," +
    1296                 "application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx," +
    1297                 "application/vnd.openxmlformats-officedocument.presentationml.template,potx," +
    1298                 "application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx," +
    1299                 "application/x-javascript,js," +
    1300                 "application/json,json," +
    1301                 "audio/mpeg,mp3 mpga mpega mp2," +
    1302                 "audio/x-wav,wav," +
    1303                 "audio/x-m4a,m4a," +
    1304                 "audio/ogg,oga ogg," +
    1305                 "audio/aiff,aiff aif," +
    1306                 "audio/flac,flac," +
    1307                 "audio/aac,aac," +
    1308                 "audio/ac3,ac3," +
    1309                 "audio/x-ms-wma,wma," +
    1310                 "image/bmp,bmp," +
    1311                 "image/gif,gif," +
    1312                 "image/jpeg,jpg jpeg jpe," +
    1313                 "image/photoshop,psd," +
    1314                 "image/png,png," +
    1315                 "image/svg+xml,svg svgz," +
    1316                 "image/tiff,tiff tif," +
    1317                 "text/plain,asc txt text diff log," +
    1318                 "text/html,htm html xhtml," +
    1319                 "text/css,css," +
    1320                 "text/csv,csv," +
    1321                 "text/rtf,rtf," +
    1322                 "video/mpeg,mpeg mpg mpe m2v," +
    1323                 "video/quicktime,qt mov," +
    1324                 "video/mp4,mp4," +
    1325                 "video/x-m4v,m4v," +
    1326                 "video/x-flv,flv," +
    1327                 "video/x-ms-wmv,wmv," +
    1328                 "video/avi,avi," +
    1329                 "video/webm,webm," +
    1330                 "video/3gpp,3gpp 3gp," +
    1331                 "video/3gpp2,3g2," +
    1332                 "video/vnd.rn-realvideo,rv," +
    1333                 "video/ogg,ogv," +
    1334                 "video/x-matroska,mkv," +
    1335                 "application/vnd.oasis.opendocument.formula-template,otf," +
    1336                 "application/octet-stream,exe";
    1337        
    1338        
    1339         var Mime = {
    1340 
    1341                 mimes: {},
    1342 
    1343                 extensions: {},
    1344 
    1345                 // Parses the default mime types string into a mimes and extensions lookup maps
    1346                 addMimeType: function (mimeData) {
    1347                         var items = mimeData.split(/,/), i, ii, ext;
    1348                        
    1349                         for (i = 0; i < items.length; i += 2) {
    1350                                 ext = items[i + 1].split(/ /);
    1351 
    1352                                 // extension to mime lookup
    1353                                 for (ii = 0; ii < ext.length; ii++) {
    1354                                         this.mimes[ext[ii]] = items[i];
    1355                                 }
    1356                                 // mime to extension lookup
    1357                                 this.extensions[items[i]] = ext;
     1656                        function ImageError(code) {
     1657                                this.code = code;
     1658                                this.name = _findKey(namecodes, code);
     1659                                this.message = this.name + ": ImageError " + this.code;
    13581660                        }
    1359                 },
    1360 
    1361 
    1362                 extList2mimes: function (filters, addMissingExtensions) {
    1363                         var self = this, ext, i, ii, type, mimes = [];
    13641661                       
    1365                         // convert extensions to mime types list
    1366                         for (i = 0; i < filters.length; i++) {
    1367                                 ext = filters[i].extensions.split(/\s*,\s*/);
     1662                        Basic.extend(ImageError, namecodes);
     1663                        ImageError.prototype = Error.prototype;
    13681664
    1369                                 for (ii = 0; ii < ext.length; ii++) {
    1370                                        
    1371                                         // if there's an asterisk in the list, then accept attribute is not required
    1372                                         if (ext[ii] === '*') {
    1373                                                 return [];
    1374                                         }
     1665                        return ImageError;
     1666                }()),
    13751667
    1376                                         type = self.mimes[ext[ii]];
    1377                                         if (type && Basic.inArray(type, mimes) === -1) {
    1378                                                 mimes.push(type);
    1379                                         }
     1668                FileException: (function() {
     1669                        var namecodes = {
     1670                                NOT_FOUND_ERR: 1,
     1671                                SECURITY_ERR: 2,
     1672                                ABORT_ERR: 3,
     1673                                NOT_READABLE_ERR: 4,
     1674                                ENCODING_ERR: 5,
     1675                                NO_MODIFICATION_ALLOWED_ERR: 6,
     1676                                INVALID_STATE_ERR: 7,
     1677                                SYNTAX_ERR: 8
     1678                        };
    13801679
    1381                                         // future browsers should filter by extension, finally
    1382                                         if (addMissingExtensions && /^\w+$/.test(ext[ii])) {
    1383                                                 mimes.push('.' + ext[ii]);
    1384                                         } else if (!type) {
    1385                                                 // if we have no type in our map, then accept all
    1386                                                 return [];
    1387                                         }
    1388                                 }
     1680                        function FileException(code) {
     1681                                this.code = code;
     1682                                this.name = _findKey(namecodes, code);
     1683                                this.message = this.name + ": FileException " + this.code;
    13891684                        }
    1390                         return mimes;
    1391                 },
    1392 
    1393 
    1394                 mimes2exts: function(mimes) {
    1395                         var self = this, exts = [];
    13961685                       
    1397                         Basic.each(mimes, function(mime) {
    1398                                 if (mime === '*') {
    1399                                         exts = [];
    1400                                         return false;
    1401                                 }
     1686                        Basic.extend(FileException, namecodes);
     1687                        FileException.prototype = Error.prototype;
     1688                        return FileException;
     1689                }()),
     1690               
     1691                DOMException: (function() {
     1692                        var namecodes = {
     1693                                INDEX_SIZE_ERR: 1,
     1694                                DOMSTRING_SIZE_ERR: 2,
     1695                                HIERARCHY_REQUEST_ERR: 3,
     1696                                WRONG_DOCUMENT_ERR: 4,
     1697                                INVALID_CHARACTER_ERR: 5,
     1698                                NO_DATA_ALLOWED_ERR: 6,
     1699                                NO_MODIFICATION_ALLOWED_ERR: 7,
     1700                                NOT_FOUND_ERR: 8,
     1701                                NOT_SUPPORTED_ERR: 9,
     1702                                INUSE_ATTRIBUTE_ERR: 10,
     1703                                INVALID_STATE_ERR: 11,
     1704                                SYNTAX_ERR: 12,
     1705                                INVALID_MODIFICATION_ERR: 13,
     1706                                NAMESPACE_ERR: 14,
     1707                                INVALID_ACCESS_ERR: 15,
     1708                                VALIDATION_ERR: 16,
     1709                                TYPE_MISMATCH_ERR: 17,
     1710                                SECURITY_ERR: 18,
     1711                                NETWORK_ERR: 19,
     1712                                ABORT_ERR: 20,
     1713                                URL_MISMATCH_ERR: 21,
     1714                                QUOTA_EXCEEDED_ERR: 22,
     1715                                TIMEOUT_ERR: 23,
     1716                                INVALID_NODE_TYPE_ERR: 24,
     1717                                DATA_CLONE_ERR: 25
     1718                        };
    14021719
    1403                                 // check if this thing looks like mime type
    1404                                 var m = mime.match(/^(\w+)\/(\*|\w+)$/);
    1405                                 if (m) {
    1406                                         if (m[2] === '*') {
    1407                                                 // wildcard mime type detected
    1408                                                 Basic.each(self.extensions, function(arr, mime) {
    1409                                                         if ((new RegExp('^' + m[1] + '/')).test(mime)) {
    1410                                                                 [].push.apply(exts, self.extensions[mime]);
    1411                                                         }
    1412                                                 });
    1413                                         } else if (self.extensions[mime]) {
    1414                                                 [].push.apply(exts, self.extensions[mime]);
    1415                                         }
    1416                                 }
    1417                         });
    1418                         return exts;
    1419                 },
    1420 
    1421 
    1422                 mimes2extList: function(mimes) {
    1423                         var accept = [], exts = [];
    1424 
    1425                         if (Basic.typeOf(mimes) === 'string') {
    1426                                 mimes = Basic.trim(mimes).split(/\s*,\s*/);
     1720                        function DOMException(code) {
     1721                                this.code = code;
     1722                                this.name = _findKey(namecodes, code);
     1723                                this.message = this.name + ": DOMException " + this.code;
    14271724                        }
    1428 
    1429                         exts = this.mimes2exts(mimes);
    14301725                       
    1431                         accept.push({
    1432                                 title: I18n.translate('Files'),
    1433                                 extensions: exts.length ? exts.join(',') : '*'
     1726                        Basic.extend(DOMException, namecodes);
     1727                        DOMException.prototype = Error.prototype;
     1728                        return DOMException;
     1729                }()),
     1730               
     1731                EventException: (function() {
     1732                        function EventException(code) {
     1733                                this.code = code;
     1734                                this.name = 'EventException';
     1735                        }
     1736                       
     1737                        Basic.extend(EventException, {
     1738                                UNSPECIFIED_EVENT_TYPE_ERR: 0
    14341739                        });
    14351740                       
    1436                         // save original mimes string
    1437                         accept.mimes = mimes;
    1438 
    1439                         return accept;
    1440                 },
    1441 
    1442 
    1443                 getFileExtension: function(fileName) {
    1444                         var matches = fileName && fileName.match(/\.([^.]+)$/);
    1445                         if (matches) {
    1446                                 return matches[1].toLowerCase();
    1447                         }
    1448                         return '';
    1449                 },
    1450 
    1451                 getFileMime: function(fileName) {
    1452                         return this.mimes[this.getFileExtension(fileName)] || '';
    1453                 }
     1741                        EventException.prototype = Error.prototype;
     1742                       
     1743                        return EventException;
     1744                }())
    14541745        };
    1455 
    1456         Mime.addMimeType(mimeData);
    1457 
    1458         return Mime;
    14591746});
    14601747
    14611748// Included from: src/javascript/core/utils/Dom.js
     
    14701757 * Contributing: http://www.plupload.com/contributing
    14711758 */
    14721759
     1760/**
     1761@class moxie/core/utils/Dom
     1762@public
     1763@static
     1764*/
     1765
    14731766define('moxie/core/utils/Dom', ['moxie/core/utils/Env'], function(Env) {
    14741767
    14751768        /**
     
    14761769        Get DOM Element by it's id.
    14771770
    14781771        @method get
    1479         @for Utils
    14801772        @param {String} id Identifier of the DOM Element
    14811773        @return {DOMElement}
    14821774        */
     
    16411933        };
    16421934});
    16431935
    1644 // Included from: src/javascript/core/Exceptions.js
     1936// Included from: src/javascript/core/EventTarget.js
    16451937
    16461938/**
    1647  * Exceptions.js
     1939 * EventTarget.js
    16481940 *
    16491941 * Copyright 2013, Moxiecode Systems AB
    16501942 * Released under GPL License.
     
    16531945 * Contributing: http://www.plupload.com/contributing
    16541946 */
    16551947
    1656 define('moxie/core/Exceptions', [
     1948define('moxie/core/EventTarget', [
     1949        'moxie/core/utils/Env',
     1950        'moxie/core/Exceptions',
    16571951        'moxie/core/utils/Basic'
    1658 ], function(Basic) {
    1659         function _findKey(obj, value) {
    1660                 var key;
    1661                 for (key in obj) {
    1662                         if (obj[key] === value) {
    1663                                 return key;
    1664                         }
    1665                 }
    1666                 return null;
     1952], function(Env, x, Basic) {
     1953
     1954        // hash of event listeners by object uid
     1955        var eventpool = {};
     1956
     1957        /**
     1958        Parent object for all event dispatching components and objects
     1959
     1960        @class moxie/core/EventTarget
     1961        @constructor EventTarget
     1962        */
     1963        function EventTarget() {
     1964                /**
     1965                Unique id of the event dispatcher, usually overriden by children
     1966
     1967                @property uid
     1968                @type String
     1969                */
     1970                this.uid = Basic.guid();
    16671971        }
    16681972
    1669         return {
    1670                 RuntimeError: (function() {
    1671                         var namecodes = {
    1672                                 NOT_INIT_ERR: 1,
    1673                                 NOT_SUPPORTED_ERR: 9,
    1674                                 JS_ERR: 4
    1675                         };
    16761973
    1677                         function RuntimeError(code) {
    1678                                 this.code = code;
    1679                                 this.name = _findKey(namecodes, code);
    1680                                 this.message = this.name + ": RuntimeError " + this.code;
    1681                         }
    1682                        
    1683                         Basic.extend(RuntimeError, namecodes);
    1684                         RuntimeError.prototype = Error.prototype;
    1685                         return RuntimeError;
    1686                 }()),
    1687                
    1688                 OperationNotAllowedException: (function() {
    1689                        
    1690                         function OperationNotAllowedException(code) {
    1691                                 this.code = code;
    1692                                 this.name = 'OperationNotAllowedException';
    1693                         }
    1694                        
    1695                         Basic.extend(OperationNotAllowedException, {
    1696                                 NOT_ALLOWED_ERR: 1
    1697                         });
    1698                        
    1699                         OperationNotAllowedException.prototype = Error.prototype;
    1700                        
    1701                         return OperationNotAllowedException;
    1702                 }()),
     1974        Basic.extend(EventTarget.prototype, {
    17031975
    1704                 ImageError: (function() {
    1705                         var namecodes = {
    1706                                 WRONG_FORMAT: 1,
    1707                                 MAX_RESOLUTION_ERR: 2,
    1708                                 INVALID_META_ERR: 3
    1709                         };
     1976                /**
     1977                Can be called from within a child  in order to acquire uniqie id in automated manner
    17101978
    1711                         function ImageError(code) {
    1712                                 this.code = code;
    1713                                 this.name = _findKey(namecodes, code);
    1714                                 this.message = this.name + ": ImageError " + this.code;
     1979                @method init
     1980                */
     1981                init: function() {
     1982                        if (!this.uid) {
     1983                                this.uid = Basic.guid('uid_');
    17151984                        }
    1716                        
    1717                         Basic.extend(ImageError, namecodes);
    1718                         ImageError.prototype = Error.prototype;
     1985                },
    17191986
    1720                         return ImageError;
    1721                 }()),
     1987                /**
     1988                Register a handler to a specific event dispatched by the object
    17221989
    1723                 FileException: (function() {
    1724                         var namecodes = {
    1725                                 NOT_FOUND_ERR: 1,
    1726                                 SECURITY_ERR: 2,
    1727                                 ABORT_ERR: 3,
    1728                                 NOT_READABLE_ERR: 4,
    1729                                 ENCODING_ERR: 5,
    1730                                 NO_MODIFICATION_ALLOWED_ERR: 6,
    1731                                 INVALID_STATE_ERR: 7,
    1732                                 SYNTAX_ERR: 8
    1733                         };
     1990                @method addEventListener
     1991                @param {String} type Type or basically a name of the event to subscribe to
     1992                @param {Function} fn Callback function that will be called when event happens
     1993                @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
     1994                @param {Object} [scope=this] A scope to invoke event handler in
     1995                */
     1996                addEventListener: function(type, fn, priority, scope) {
     1997                        var self = this, list;
    17341998
    1735                         function FileException(code) {
    1736                                 this.code = code;
    1737                                 this.name = _findKey(namecodes, code);
    1738                                 this.message = this.name + ": FileException " + this.code;
     1999                        // without uid no event handlers can be added, so make sure we got one
     2000                        if (!this.hasOwnProperty('uid')) {
     2001                                this.uid = Basic.guid('uid_');
    17392002                        }
    1740                        
    1741                         Basic.extend(FileException, namecodes);
    1742                         FileException.prototype = Error.prototype;
    1743                         return FileException;
    1744                 }()),
    1745                
    1746                 DOMException: (function() {
    1747                         var namecodes = {
    1748                                 INDEX_SIZE_ERR: 1,
    1749                                 DOMSTRING_SIZE_ERR: 2,
    1750                                 HIERARCHY_REQUEST_ERR: 3,
    1751                                 WRONG_DOCUMENT_ERR: 4,
    1752                                 INVALID_CHARACTER_ERR: 5,
    1753                                 NO_DATA_ALLOWED_ERR: 6,
    1754                                 NO_MODIFICATION_ALLOWED_ERR: 7,
    1755                                 NOT_FOUND_ERR: 8,
    1756                                 NOT_SUPPORTED_ERR: 9,
    1757                                 INUSE_ATTRIBUTE_ERR: 10,
    1758                                 INVALID_STATE_ERR: 11,
    1759                                 SYNTAX_ERR: 12,
    1760                                 INVALID_MODIFICATION_ERR: 13,
    1761                                 NAMESPACE_ERR: 14,
    1762                                 INVALID_ACCESS_ERR: 15,
    1763                                 VALIDATION_ERR: 16,
    1764                                 TYPE_MISMATCH_ERR: 17,
    1765                                 SECURITY_ERR: 18,
    1766                                 NETWORK_ERR: 19,
    1767                                 ABORT_ERR: 20,
    1768                                 URL_MISMATCH_ERR: 21,
    1769                                 QUOTA_EXCEEDED_ERR: 22,
    1770                                 TIMEOUT_ERR: 23,
    1771                                 INVALID_NODE_TYPE_ERR: 24,
    1772                                 DATA_CLONE_ERR: 25
    1773                         };
    17742003
    1775                         function DOMException(code) {
    1776                                 this.code = code;
    1777                                 this.name = _findKey(namecodes, code);
    1778                                 this.message = this.name + ": DOMException " + this.code;
     2004                        type = Basic.trim(type);
     2005
     2006                        if (/\s/.test(type)) {
     2007                                // multiple event types were passed for one handler
     2008                                Basic.each(type.split(/\s+/), function(type) {
     2009                                        self.addEventListener(type, fn, priority, scope);
     2010                                });
     2011                                return;
    17792012                        }
    1780                        
    1781                         Basic.extend(DOMException, namecodes);
    1782                         DOMException.prototype = Error.prototype;
    1783                         return DOMException;
    1784                 }()),
    1785                
    1786                 EventException: (function() {
    1787                         function EventException(code) {
    1788                                 this.code = code;
    1789                                 this.name = 'EventException';
    1790                         }
    1791                        
    1792                         Basic.extend(EventException, {
    1793                                 UNSPECIFIED_EVENT_TYPE_ERR: 0
    1794                         });
    1795                        
    1796                         EventException.prototype = Error.prototype;
    1797                        
    1798                         return EventException;
    1799                 }())
    1800         };
    1801 });
    18022013
    1803 // Included from: src/javascript/core/EventTarget.js
     2014                        type = type.toLowerCase();
     2015                        priority = parseInt(priority, 10) || 0;
    18042016
    1805 /**
    1806  * EventTarget.js
    1807  *
    1808  * Copyright 2013, Moxiecode Systems AB
    1809  * Released under GPL License.
    1810  *
    1811  * License: http://www.plupload.com/license
    1812  * Contributing: http://www.plupload.com/contributing
    1813  */
     2017                        list = eventpool[this.uid] && eventpool[this.uid][type] || [];
     2018                        list.push({fn : fn, priority : priority, scope : scope || this});
    18142019
    1815 define('moxie/core/EventTarget', [
    1816         'moxie/core/utils/Env',
    1817         'moxie/core/Exceptions',
    1818         'moxie/core/utils/Basic'
    1819 ], function(Env, x, Basic) {
    1820         /**
    1821         Parent object for all event dispatching components and objects
     2020                        if (!eventpool[this.uid]) {
     2021                                eventpool[this.uid] = {};
     2022                        }
     2023                        eventpool[this.uid][type] = list;
     2024                },
    18222025
    1823         @class EventTarget
    1824         @constructor EventTarget
    1825         */
    1826         function EventTarget() {
    1827                 // hash of event listeners by object uid
    1828                 var eventpool = {};
    1829                                
    1830                 Basic.extend(this, {
    1831                        
    1832                         /**
    1833                         Unique id of the event dispatcher, usually overriden by children
     2026                /**
     2027                Check if any handlers were registered to the specified event
    18342028
    1835                         @property uid
    1836                         @type String
    1837                         */
    1838                         uid: null,
    1839                        
    1840                         /**
    1841                         Can be called from within a child  in order to acquire uniqie id in automated manner
     2029                @method hasEventListener
     2030                @param {String} [type] Type or basically a name of the event to check
     2031                @return {Mixed} Returns a handler if it was found and false, if - not
     2032                */
     2033                hasEventListener: function(type) {
     2034                        var list;
     2035                        if (type) {
     2036                                type = type.toLowerCase();
     2037                                list = eventpool[this.uid] && eventpool[this.uid][type];
     2038                        } else {
     2039                                list = eventpool[this.uid];
     2040                        }
     2041                        return list ? list : false;
     2042                },
    18422043
    1843                         @method init
    1844                         */
    1845                         init: function() {
    1846                                 if (!this.uid) {
    1847                                         this.uid = Basic.guid('uid_');
    1848                                 }
    1849                         },
     2044                /**
     2045                Unregister the handler from the event, or if former was not specified - unregister all handlers
    18502046
    1851                         /**
    1852                         Register a handler to a specific event dispatched by the object
     2047                @method removeEventListener
     2048                @param {String} type Type or basically a name of the event
     2049                @param {Function} [fn] Handler to unregister
     2050                */
     2051                removeEventListener: function(type, fn) {
     2052                        var self = this, list, i;
    18532053
    1854                         @method addEventListener
    1855                         @param {String} type Type or basically a name of the event to subscribe to
    1856                         @param {Function} fn Callback function that will be called when event happens
    1857                         @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
    1858                         @param {Object} [scope=this] A scope to invoke event handler in
    1859                         */
    1860                         addEventListener: function(type, fn, priority, scope) {
    1861                                 var self = this, list;
     2054                        type = type.toLowerCase();
    18622055
    1863                                 // without uid no event handlers can be added, so make sure we got one
    1864                                 if (!this.hasOwnProperty('uid')) {
    1865                                         this.uid = Basic.guid('uid_');
    1866                                 }
    1867                                
    1868                                 type = Basic.trim(type);
    1869                                
    1870                                 if (/\s/.test(type)) {
    1871                                         // multiple event types were passed for one handler
    1872                                         Basic.each(type.split(/\s+/), function(type) {
    1873                                                 self.addEventListener(type, fn, priority, scope);
    1874                                         });
    1875                                         return;
    1876                                 }
    1877                                
    1878                                 type = type.toLowerCase();
    1879                                 priority = parseInt(priority, 10) || 0;
    1880                                
    1881                                 list = eventpool[this.uid] && eventpool[this.uid][type] || [];
    1882                                 list.push({fn : fn, priority : priority, scope : scope || this});
    1883                                
    1884                                 if (!eventpool[this.uid]) {
    1885                                         eventpool[this.uid] = {};
    1886                                 }
    1887                                 eventpool[this.uid][type] = list;
    1888                         },
    1889                        
    1890                         /**
    1891                         Check if any handlers were registered to the specified event
     2056                        if (/\s/.test(type)) {
     2057                                // multiple event types were passed for one handler
     2058                                Basic.each(type.split(/\s+/), function(type) {
     2059                                        self.removeEventListener(type, fn);
     2060                                });
     2061                                return;
     2062                        }
    18922063
    1893                         @method hasEventListener
    1894                         @param {String} type Type or basically a name of the event to check
    1895                         @return {Mixed} Returns a handler if it was found and false, if - not
    1896                         */
    1897                         hasEventListener: function(type) {
    1898                                 var list = type ? eventpool[this.uid] && eventpool[this.uid][type] : eventpool[this.uid];
    1899                                 return list ? list : false;
    1900                         },
    1901                        
    1902                         /**
    1903                         Unregister the handler from the event, or if former was not specified - unregister all handlers
     2064                        list = eventpool[this.uid] && eventpool[this.uid][type];
    19042065
    1905                         @method removeEventListener
    1906                         @param {String} type Type or basically a name of the event
    1907                         @param {Function} [fn] Handler to unregister
    1908                         */
    1909                         removeEventListener: function(type, fn) {
    1910                                 type = type.toLowerCase();
    1911        
    1912                                 var list = eventpool[this.uid] && eventpool[this.uid][type], i;
    1913        
    1914                                 if (list) {
    1915                                         if (fn) {
    1916                                                 for (i = list.length - 1; i >= 0; i--) {
    1917                                                         if (list[i].fn === fn) {
    1918                                                                 list.splice(i, 1);
    1919                                                                 break;
    1920                                                         }
     2066                        if (list) {
     2067                                if (fn) {
     2068                                        for (i = list.length - 1; i >= 0; i--) {
     2069                                                if (list[i].fn === fn) {
     2070                                                        list.splice(i, 1);
     2071                                                        break;
    19212072                                                }
    1922                                         } else {
    1923                                                 list = [];
    19242073                                        }
    1925        
    1926                                         // delete event list if it has become empty
    1927                                         if (!list.length) {
    1928                                                 delete eventpool[this.uid][type];
    1929                                                
    1930                                                 // and object specific entry in a hash if it has no more listeners attached
    1931                                                 if (Basic.isEmptyObj(eventpool[this.uid])) {
    1932                                                         delete eventpool[this.uid];
    1933                                                 }
    1934                                         }
     2074                                } else {
     2075                                        list = [];
    19352076                                }
    1936                         },
    1937                        
    1938                         /**
    1939                         Remove all event handlers from the object
    19402077
    1941                         @method removeAllEventListeners
    1942                         */
    1943                         removeAllEventListeners: function() {
    1944                                 if (eventpool[this.uid]) {
    1945                                         delete eventpool[this.uid];
     2078                                // delete event list if it has become empty
     2079                                if (!list.length) {
     2080                                        delete eventpool[this.uid][type];
     2081
     2082                                        // and object specific entry in a hash if it has no more listeners attached
     2083                                        if (Basic.isEmptyObj(eventpool[this.uid])) {
     2084                                                delete eventpool[this.uid];
     2085                                        }
    19462086                                }
    1947                         },
    1948                        
    1949                         /**
    1950                         Dispatch the event
     2087                        }
     2088                },
    19512089
    1952                         @method dispatchEvent
    1953                         @param {String/Object} Type of event or event object to dispatch
    1954                         @param {Mixed} [...] Variable number of arguments to be passed to a handlers
    1955                         @return {Boolean} true by default and false if any handler returned false
    1956                         */
    1957                         dispatchEvent: function(type) {
    1958                                 var uid, list, args, tmpEvt, evt = {}, result = true, undef;
    1959                                
    1960                                 if (Basic.typeOf(type) !== 'string') {
    1961                                         // we can't use original object directly (because of Silverlight)
    1962                                         tmpEvt = type;
     2090                /**
     2091                Remove all event handlers from the object
    19632092
    1964                                         if (Basic.typeOf(tmpEvt.type) === 'string') {
    1965                                                 type = tmpEvt.type;
     2093                @method removeAllEventListeners
     2094                */
     2095                removeAllEventListeners: function() {
     2096                        if (eventpool[this.uid]) {
     2097                                delete eventpool[this.uid];
     2098                        }
     2099                },
    19662100
    1967                                                 if (tmpEvt.total !== undef && tmpEvt.loaded !== undef) { // progress event
    1968                                                         evt.total = tmpEvt.total;
    1969                                                         evt.loaded = tmpEvt.loaded;
    1970                                                 }
    1971                                                 evt.async = tmpEvt.async || false;
    1972                                         } else {
    1973                                                 throw new x.EventException(x.EventException.UNSPECIFIED_EVENT_TYPE_ERR);
     2101                /**
     2102                Dispatch the event
     2103
     2104                @method dispatchEvent
     2105                @param {String/Object} Type of event or event object to dispatch
     2106                @param {Mixed} [...] Variable number of arguments to be passed to a handlers
     2107                @return {Boolean} true by default and false if any handler returned false
     2108                */
     2109                dispatchEvent: function(type) {
     2110                        var uid, list, args, tmpEvt, evt = {}, result = true, undef;
     2111
     2112                        if (Basic.typeOf(type) !== 'string') {
     2113                                // we can't use original object directly (because of Silverlight)
     2114                                tmpEvt = type;
     2115
     2116                                if (Basic.typeOf(tmpEvt.type) === 'string') {
     2117                                        type = tmpEvt.type;
     2118
     2119                                        if (tmpEvt.total !== undef && tmpEvt.loaded !== undef) { // progress event
     2120                                                evt.total = tmpEvt.total;
     2121                                                evt.loaded = tmpEvt.loaded;
    19742122                                        }
    1975                                 }
    1976                                
    1977                                 // check if event is meant to be dispatched on an object having specific uid
    1978                                 if (type.indexOf('::') !== -1) {
    1979                                         (function(arr) {
    1980                                                 uid = arr[0];
    1981                                                 type = arr[1];
    1982                                         }(type.split('::')));
     2123                                        evt.async = tmpEvt.async || false;
    19832124                                } else {
    1984                                         uid = this.uid;
     2125                                        throw new x.EventException(x.EventException.UNSPECIFIED_EVENT_TYPE_ERR);
    19852126                                }
    1986                                
    1987                                 type = type.toLowerCase();
    1988                                                                
    1989                                 list = eventpool[uid] && eventpool[uid][type];
     2127                        }
    19902128
    1991                                 if (list) {
    1992                                         // sort event list by prority
    1993                                         list.sort(function(a, b) { return b.priority - a.priority; });
    1994                                        
    1995                                         args = [].slice.call(arguments);
    1996                                        
    1997                                         // first argument will be pseudo-event object
    1998                                         args.shift();
    1999                                         evt.type = type;
    2000                                         args.unshift(evt);
     2129                        // check if event is meant to be dispatched on an object having specific uid
     2130                        if (type.indexOf('::') !== -1) {
     2131                                (function(arr) {
     2132                                        uid = arr[0];
     2133                                        type = arr[1];
     2134                                }(type.split('::')));
     2135                        } else {
     2136                                uid = this.uid;
     2137                        }
    20012138
    2002                                         if (MXI_DEBUG && Env.debug.events) {
    2003                                                 Env.log("Event '%s' fired on %u", evt.type, uid);       
    2004                                         }
     2139                        type = type.toLowerCase();
    20052140
    2006                                         // Dispatch event to all listeners
    2007                                         var queue = [];
    2008                                         Basic.each(list, function(handler) {
    2009                                                 // explicitly set the target, otherwise events fired from shims do not get it
    2010                                                 args[0].target = handler.scope;
    2011                                                 // if event is marked as async, detach the handler
    2012                                                 if (evt.async) {
    2013                                                         queue.push(function(cb) {
    2014                                                                 setTimeout(function() {
    2015                                                                         cb(handler.fn.apply(handler.scope, args) === false);
    2016                                                                 }, 1);
    2017                                                         });
    2018                                                 } else {
    2019                                                         queue.push(function(cb) {
    2020                                                                 cb(handler.fn.apply(handler.scope, args) === false); // if handler returns false stop propagation
    2021                                                         });
    2022                                                 }
    2023                                         });
    2024                                         if (queue.length) {
    2025                                                 Basic.inSeries(queue, function(err) {
    2026                                                         result = !err;
     2141                        list = eventpool[uid] && eventpool[uid][type];
     2142
     2143                        if (list) {
     2144                                // sort event list by prority
     2145                                list.sort(function(a, b) { return b.priority - a.priority; });
     2146
     2147                                args = [].slice.call(arguments);
     2148
     2149                                // first argument will be pseudo-event object
     2150                                args.shift();
     2151                                evt.type = type;
     2152                                args.unshift(evt);
     2153
     2154                                if (MXI_DEBUG && Env.debug.events) {
     2155                                        Env.log("%cEvent '%s' fired on %s", 'color: #999;', evt.type, (this.ctorName ? this.ctorName + '::' : '') + uid);
     2156                                }
     2157
     2158                                // Dispatch event to all listeners
     2159                                var queue = [];
     2160                                Basic.each(list, function(handler) {
     2161                                        // explicitly set the target, otherwise events fired from shims do not get it
     2162                                        args[0].target = handler.scope;
     2163                                        // if event is marked as async, detach the handler
     2164                                        if (evt.async) {
     2165                                                queue.push(function(cb) {
     2166                                                        setTimeout(function() {
     2167                                                                cb(handler.fn.apply(handler.scope, args) === false);
     2168                                                        }, 1);
    20272169                                                });
     2170                                        } else {
     2171                                                queue.push(function(cb) {
     2172                                                        cb(handler.fn.apply(handler.scope, args) === false); // if handler returns false stop propagation
     2173                                                });
    20282174                                        }
     2175                                });
     2176                                if (queue.length) {
     2177                                        Basic.inSeries(queue, function(err) {
     2178                                                result = !err;
     2179                                        });
    20292180                                }
    2030                                 return result;
    2031                         },
    2032                        
    2033                         /**
    2034                         Alias for addEventListener
     2181                        }
     2182                        return result;
     2183                },
    20352184
    2036                         @method bind
    2037                         @protected
    2038                         */
    2039                         bind: function() {
    2040                                 this.addEventListener.apply(this, arguments);
    2041                         },
    2042                        
    2043                         /**
    2044                         Alias for removeEventListener
     2185                /**
     2186                Register a handler to the event type that will run only once
    20452187
    2046                         @method unbind
    2047                         @protected
    2048                         */
    2049                         unbind: function() {
    2050                                 this.removeEventListener.apply(this, arguments);
    2051                         },
    2052                        
    2053                         /**
    2054                         Alias for removeAllEventListeners
     2188                @method bindOnce
     2189                @since >1.4.1
     2190                @param {String} type Type or basically a name of the event to subscribe to
     2191                @param {Function} fn Callback function that will be called when event happens
     2192                @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
     2193                @param {Object} [scope=this] A scope to invoke event handler in
     2194                */
     2195                bindOnce: function(type, fn, priority, scope) {
     2196                        var self = this;
     2197                        self.bind.call(this, type, function cb() {
     2198                                self.unbind(type, cb);
     2199                                return fn.apply(this, arguments);
     2200                        }, priority, scope);
     2201                },
    20552202
    2056                         @method unbindAll
    2057                         @protected
    2058                         */
    2059                         unbindAll: function() {
    2060                                 this.removeAllEventListeners.apply(this, arguments);
    2061                         },
    2062                        
    2063                         /**
    2064                         Alias for dispatchEvent
     2203                /**
     2204                Alias for addEventListener
    20652205
    2066                         @method trigger
    2067                         @protected
    2068                         */
    2069                         trigger: function() {
    2070                                 return this.dispatchEvent.apply(this, arguments);
    2071                         },
    2072                        
     2206                @method bind
     2207                @protected
     2208                */
     2209                bind: function() {
     2210                        this.addEventListener.apply(this, arguments);
     2211                },
    20732212
    2074                         /**
    2075                         Handle properties of on[event] type.
     2213                /**
     2214                Alias for removeEventListener
    20762215
    2077                         @method handleEventProps
    2078                         @private
    2079                         */
    2080                         handleEventProps: function(dispatches) {
    2081                                 var self = this;
     2216                @method unbind
     2217                @protected
     2218                */
     2219                unbind: function() {
     2220                        this.removeEventListener.apply(this, arguments);
     2221                },
    20822222
    2083                                 this.bind(dispatches.join(' '), function(e) {
    2084                                         var prop = 'on' + e.type.toLowerCase();
    2085                                         if (Basic.typeOf(this[prop]) === 'function') {
    2086                                                 this[prop].apply(this, arguments);
    2087                                         }
    2088                                 });
     2223                /**
     2224                Alias for removeAllEventListeners
    20892225
    2090                                 // object must have defined event properties, even if it doesn't make use of them
    2091                                 Basic.each(dispatches, function(prop) {
    2092                                         prop = 'on' + prop.toLowerCase(prop);
    2093                                         if (Basic.typeOf(self[prop]) === 'undefined') {
    2094                                                 self[prop] = null;
    2095                                         }
    2096                                 });
    2097                         }
    2098                        
    2099                 });
    2100         }
     2226                @method unbindAll
     2227                @protected
     2228                */
     2229                unbindAll: function() {
     2230                        this.removeAllEventListeners.apply(this, arguments);
     2231                },
    21012232
    2102         EventTarget.instance = new EventTarget();
     2233                /**
     2234                Alias for dispatchEvent
    21032235
     2236                @method trigger
     2237                @protected
     2238                */
     2239                trigger: function() {
     2240                        return this.dispatchEvent.apply(this, arguments);
     2241                },
     2242
     2243
     2244                /**
     2245                Handle properties of on[event] type.
     2246
     2247                @method handleEventProps
     2248                @private
     2249                */
     2250                handleEventProps: function(dispatches) {
     2251                        var self = this;
     2252
     2253                        this.bind(dispatches.join(' '), function(e) {
     2254                                var prop = 'on' + e.type.toLowerCase();
     2255                                if (Basic.typeOf(this[prop]) === 'function') {
     2256                                        this[prop].apply(this, arguments);
     2257                                }
     2258                        });
     2259
     2260                        // object must have defined event properties, even if it doesn't make use of them
     2261                        Basic.each(dispatches, function(prop) {
     2262                                prop = 'on' + prop.toLowerCase(prop);
     2263                                if (Basic.typeOf(self[prop]) === 'undefined') {
     2264                                        self[prop] = null;
     2265                                }
     2266                        });
     2267                }
     2268
     2269        });
     2270
     2271
     2272        EventTarget.instance = new EventTarget();
     2273
    21042274        return EventTarget;
    21052275});
    21062276
     
    21272297        /**
    21282298        Common set of methods and properties for every runtime instance
    21292299
    2130         @class Runtime
     2300        @class moxie/runtime/Runtime
    21312301
    21322302        @param {Object} options
    21332303        @param {String} type Sanitized name of the runtime
     
    23712541
    23722542                                // if no container for shim, create one
    23732543                                if (!shimContainer) {
    2374                                         container = this.options.container ? Dom.get(this.options.container) : document.body;
     2544                                        container = Dom.get(this.options.container) || document.body;
    23752545
    23762546                                        // create shim container and insert it at an absolute position into the outer container
    23772547                                        shimContainer = document.createElement('div');
     
    24752645        @type String
    24762646        @static
    24772647        */
    2478         Runtime.order = 'html5,html4';
     2648        Runtime.order = 'html5,flash,silverlight,html4';
    24792649
    24802650
    24812651        /**
     
    26462816                                                // if cap requires conflicting mode - runtime cannot fulfill required caps
    26472817
    26482818                                                if (MXI_DEBUG && Env.debug.runtime) {
    2649                                                         Env.log("\t\t%c: %v (conflicting mode requested: %s)", cap, value, capMode);   
     2819                                                        Env.log("\t\t%s: %s (conflicting mode requested: %s)", cap, value, capMode);
    26502820                                                }
    26512821
    26522822                                                return (mode = false);
     
    26542824                                }
    26552825
    26562826                                if (MXI_DEBUG && Env.debug.runtime) {
    2657                                         Env.log("\t\t%c: %v (compatible modes: %s)", cap, value, mode);
     2827                                        Env.log("\t\t%s: %s (compatible modes: %s)", cap, value, mode);
    26582828                                }
    26592829                        });
    26602830
     
    26692839
    26702840
    26712841        /**
     2842         * Third party shims (Flash and Silverlight) require global event target against which they
     2843         * will fire their events. However when moxie is not loaded to global namespace, default
     2844         * event target is not accessible and we have to create artificial ones.
     2845         *
     2846         * @method getGlobalEventTarget
     2847         * @static
     2848         * @return {String} Name of the global event target
     2849         */
     2850        Runtime.getGlobalEventTarget = function() {
     2851                if (/^moxie\./.test(Env.global_event_dispatcher) && !Env.can('access_global_ns')) {
     2852                        var uniqueCallbackName = Basic.guid('moxie_event_target_');
     2853
     2854                        window[uniqueCallbackName] = function(e, data) {
     2855                                EventTarget.instance.dispatchEvent(e, data);
     2856                        };
     2857
     2858                        Env.global_event_dispatcher = uniqueCallbackName;
     2859                }
     2860
     2861                return Env.global_event_dispatcher;
     2862        };
     2863
     2864
     2865        /**
    26722866        Capability check that always returns true
    26732867
    26742868        @private
     
    27282922        /**
    27292923        Set of methods and properties, required by a component to acquire ability to connect to a runtime
    27302924
    2731         @class RuntimeClient
     2925        @class moxie/runtime/RuntimeClient
    27322926        */
    27332927        return function RuntimeClient() {
    27342928                var runtime;
     
    27582952                                        type = items.shift().toLowerCase();
    27592953                                        constructor = Runtime.getConstructor(type);
    27602954                                        if (!constructor) {
     2955                                                if (MXI_DEBUG && Env.debug.runtime) {
     2956                                                        Env.log("Constructor for '%s' runtime is not available.", type);
     2957                                                }
    27612958                                                initialize(items);
    27622959                                                return;
    27632960                                        }
     
    27812978                                                // jailbreak ...
    27822979                                                setTimeout(function() {
    27832980                                                        runtime.clients++;
     2981                                                        comp.ruid = runtime.uid;
    27842982                                                        // this will be triggered on component
    27852983                                                        comp.trigger('RuntimeInit', runtime);
    27862984                                                }, 1);
     
    27952993                                                initialize(items);
    27962994                                        });
    27972995
    2798                                         /*runtime.bind('Exception', function() { });*/
     2996                                        runtime.bind('Exception', function(e, err) {
     2997                                                var message = err.name + "(#" + err.code + ")" + (err.message ? ", from: " + err.message : '');
     2998                                               
     2999                                                if (MXI_DEBUG && Env.debug.runtime) {
     3000                                                        Env.log("Runtime '%s' has thrown an exception: %s", this.type, message);
     3001                                                }
     3002                                                comp.trigger('RuntimeError', new x.RuntimeError(x.RuntimeError.EXCEPTION_ERR, message));
     3003                                        });
    27993004
    28003005                                        if (MXI_DEBUG && Env.debug.runtime) {
    28013006                                                Env.log("\tselected mode: %s", runtime.mode);   
     
    28203025                                if (ruid) {
    28213026                                        runtime = Runtime.getRuntime(ruid);
    28223027                                        if (runtime) {
     3028                                                comp.ruid = ruid;
    28233029                                                runtime.clients++;
    28243030                                                return runtime;
    28253031                                        } else {
     
    28713077                        @return {Mixed} Whatever runtime extension method returns
    28723078                        */
    28733079                        exec: function() {
    2874                                 if (runtime) {
    2875                                         return runtime.exec.apply(this, arguments);
     3080                                return runtime ? runtime.exec.apply(this, arguments) : null;
     3081                        },
     3082
     3083
     3084                        /**
     3085                        Test runtime client for specific capability
     3086                       
     3087                        @method can
     3088                        @param {String} cap
     3089                        @return {Bool}
     3090                        */
     3091                        can: function(cap) {
     3092                                return runtime ? runtime.can(cap) : false;
     3093                        }
     3094
     3095                });
     3096        };
     3097
     3098
     3099});
     3100
     3101// Included from: src/javascript/file/Blob.js
     3102
     3103/**
     3104 * Blob.js
     3105 *
     3106 * Copyright 2013, Moxiecode Systems AB
     3107 * Released under GPL License.
     3108 *
     3109 * License: http://www.plupload.com/license
     3110 * Contributing: http://www.plupload.com/contributing
     3111 */
     3112
     3113define('moxie/file/Blob', [
     3114        'moxie/core/utils/Basic',
     3115        'moxie/core/utils/Encode',
     3116        'moxie/runtime/RuntimeClient'
     3117], function(Basic, Encode, RuntimeClient) {
     3118       
     3119        var blobpool = {};
     3120
     3121        /**
     3122        @class moxie/file/Blob
     3123        @constructor
     3124        @param {String} ruid Unique id of the runtime, to which this blob belongs to
     3125        @param {Object} blob Object "Native" blob object, as it is represented in the runtime
     3126        */
     3127        function Blob(ruid, blob) {
     3128
     3129                function _sliceDetached(start, end, type) {
     3130                        var blob, data = blobpool[this.uid];
     3131
     3132                        if (Basic.typeOf(data) !== 'string' || !data.length) {
     3133                                return null; // or throw exception
     3134                        }
     3135
     3136                        blob = new Blob(null, {
     3137                                type: type,
     3138                                size: end - start
     3139                        });
     3140                        blob.detach(data.substr(start, blob.size));
     3141
     3142                        return blob;
     3143                }
     3144
     3145                RuntimeClient.call(this);
     3146
     3147                if (ruid) {     
     3148                        this.connectRuntime(ruid);
     3149                }
     3150
     3151                if (!blob) {
     3152                        blob = {};
     3153                } else if (Basic.typeOf(blob) === 'string') { // dataUrl or binary string
     3154                        blob = { data: blob };
     3155                }
     3156
     3157                Basic.extend(this, {
     3158                       
     3159                        /**
     3160                        Unique id of the component
     3161
     3162                        @property uid
     3163                        @type {String}
     3164                        */
     3165                        uid: blob.uid || Basic.guid('uid_'),
     3166                       
     3167                        /**
     3168                        Unique id of the connected runtime, if falsy, then runtime will have to be initialized
     3169                        before this Blob can be used, modified or sent
     3170
     3171                        @property ruid
     3172                        @type {String}
     3173                        */
     3174                        ruid: ruid,
     3175       
     3176                        /**
     3177                        Size of blob
     3178
     3179                        @property size
     3180                        @type {Number}
     3181                        @default 0
     3182                        */
     3183                        size: blob.size || 0,
     3184                       
     3185                        /**
     3186                        Mime type of blob
     3187
     3188                        @property type
     3189                        @type {String}
     3190                        @default ''
     3191                        */
     3192                        type: blob.type || '',
     3193                       
     3194                        /**
     3195                        @method slice
     3196                        @param {Number} [start=0]
     3197                        */
     3198                        slice: function(start, end, type) {             
     3199                                if (this.isDetached()) {
     3200                                        return _sliceDetached.apply(this, arguments);
    28763201                                }
    2877                                 return null;
     3202                                return this.getRuntime().exec.call(this, 'Blob', 'slice', this.getSource(), start, end, type);
     3203                        },
     3204
     3205                        /**
     3206                        Returns "native" blob object (as it is represented in connected runtime) or null if not found
     3207
     3208                        @method getSource
     3209                        @return {Blob} Returns "native" blob object or null if not found
     3210                        */
     3211                        getSource: function() {
     3212                                if (!blobpool[this.uid]) {
     3213                                        return null;   
     3214                                }
     3215                                return blobpool[this.uid];
     3216                        },
     3217
     3218                        /**
     3219                        Detaches blob from any runtime that it depends on and initialize with standalone value
     3220
     3221                        @method detach
     3222                        @protected
     3223                        @param {DOMString} [data=''] Standalone value
     3224                        */
     3225                        detach: function(data) {
     3226                                if (this.ruid) {
     3227                                        this.getRuntime().exec.call(this, 'Blob', 'destroy');
     3228                                        this.disconnectRuntime();
     3229                                        this.ruid = null;
     3230                                }
     3231
     3232                                data = data || '';
     3233
     3234                                // if dataUrl, convert to binary string
     3235                                if (data.substr(0, 5) == 'data:') {
     3236                                        var base64Offset = data.indexOf(';base64,');
     3237                                        this.type = data.substring(5, base64Offset);
     3238                                        data = Encode.atob(data.substring(base64Offset + 8));
     3239                                }
     3240
     3241                                this.size = data.length;
     3242
     3243                                blobpool[this.uid] = data;
     3244                        },
     3245
     3246                        /**
     3247                        Checks if blob is standalone (detached of any runtime)
     3248                       
     3249                        @method isDetached
     3250                        @protected
     3251                        @return {Boolean}
     3252                        */
     3253                        isDetached: function() {
     3254                                return !this.ruid && Basic.typeOf(blobpool[this.uid]) === 'string';
     3255                        },
     3256                       
     3257                        /**
     3258                        Destroy Blob and free any resources it was using
     3259
     3260                        @method destroy
     3261                        */
     3262                        destroy: function() {
     3263                                this.detach();
     3264                                delete blobpool[this.uid];
    28783265                        }
     3266                });
    28793267
     3268               
     3269                if (blob.data) {
     3270                        this.detach(blob.data); // auto-detach if payload has been passed
     3271                } else {
     3272                        blobpool[this.uid] = blob;     
     3273                }
     3274        }
     3275       
     3276        return Blob;
     3277});
     3278
     3279// Included from: src/javascript/core/I18n.js
     3280
     3281/**
     3282 * I18n.js
     3283 *
     3284 * Copyright 2013, Moxiecode Systems AB
     3285 * Released under GPL License.
     3286 *
     3287 * License: http://www.plupload.com/license
     3288 * Contributing: http://www.plupload.com/contributing
     3289 */
     3290
     3291define("moxie/core/I18n", [
     3292        "moxie/core/utils/Basic"
     3293], function(Basic) {
     3294        var i18n = {};
     3295
     3296        /**
     3297        @class moxie/core/I18n
     3298        */
     3299        return {
     3300                /**
     3301                 * Extends the language pack object with new items.
     3302                 *
     3303                 * @param {Object} pack Language pack items to add.
     3304                 * @return {Object} Extended language pack object.
     3305                 */
     3306                addI18n: function(pack) {
     3307                        return Basic.extend(i18n, pack);
     3308                },
     3309
     3310                /**
     3311                 * Translates the specified string by checking for the english string in the language pack lookup.
     3312                 *
     3313                 * @param {String} str String to look for.
     3314                 * @return {String} Translated string or the input string if it wasn't found.
     3315                 */
     3316                translate: function(str) {
     3317                        return i18n[str] || str;
     3318                },
     3319
     3320                /**
     3321                 * Shortcut for translate function
     3322                 *
     3323                 * @param {String} str String to look for.
     3324                 * @return {String} Translated string or the input string if it wasn't found.
     3325                 */
     3326                _: function(str) {
     3327                        return this.translate(str);
     3328                },
     3329
     3330                /**
     3331                 * Pseudo sprintf implementation - simple way to replace tokens with specified values.
     3332                 *
     3333                 * @param {String} str String with tokens
     3334                 * @return {String} String with replaced tokens
     3335                 */
     3336                sprintf: function(str) {
     3337                        var args = [].slice.call(arguments, 1);
     3338
     3339                        return str.replace(/%[a-z]/g, function() {
     3340                                var value = args.shift();
     3341                                return Basic.typeOf(value) !== 'undefined' ? value : '';
     3342                        });
     3343                }
     3344        };
     3345});
     3346
     3347// Included from: src/javascript/core/utils/Mime.js
     3348
     3349/**
     3350 * Mime.js
     3351 *
     3352 * Copyright 2013, Moxiecode Systems AB
     3353 * Released under GPL License.
     3354 *
     3355 * License: http://www.plupload.com/license
     3356 * Contributing: http://www.plupload.com/contributing
     3357 */
     3358
     3359/**
     3360@class moxie/core/utils/Mime
     3361@public
     3362@static
     3363*/
     3364
     3365define("moxie/core/utils/Mime", [
     3366        "moxie/core/utils/Basic",
     3367        "moxie/core/I18n"
     3368], function(Basic, I18n) {
     3369
     3370        var mimeData = "" +
     3371                "application/msword,doc dot," +
     3372                "application/pdf,pdf," +
     3373                "application/pgp-signature,pgp," +
     3374                "application/postscript,ps ai eps," +
     3375                "application/rtf,rtf," +
     3376                "application/vnd.ms-excel,xls xlb xlt xla," +
     3377                "application/vnd.ms-powerpoint,ppt pps pot ppa," +
     3378                "application/zip,zip," +
     3379                "application/x-shockwave-flash,swf swfl," +
     3380                "application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx," +
     3381                "application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx," +
     3382                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx," +
     3383                "application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx," +
     3384                "application/vnd.openxmlformats-officedocument.presentationml.template,potx," +
     3385                "application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx," +
     3386                "application/x-javascript,js," +
     3387                "application/json,json," +
     3388                "audio/mpeg,mp3 mpga mpega mp2," +
     3389                "audio/x-wav,wav," +
     3390                "audio/x-m4a,m4a," +
     3391                "audio/ogg,oga ogg," +
     3392                "audio/aiff,aiff aif," +
     3393                "audio/flac,flac," +
     3394                "audio/aac,aac," +
     3395                "audio/ac3,ac3," +
     3396                "audio/x-ms-wma,wma," +
     3397                "image/bmp,bmp," +
     3398                "image/gif,gif," +
     3399                "image/jpeg,jpg jpeg jpe," +
     3400                "image/photoshop,psd," +
     3401                "image/png,png," +
     3402                "image/svg+xml,svg svgz," +
     3403                "image/tiff,tiff tif," +
     3404                "text/plain,asc txt text diff log," +
     3405                "text/html,htm html xhtml," +
     3406                "text/css,css," +
     3407                "text/csv,csv," +
     3408                "text/rtf,rtf," +
     3409                "video/mpeg,mpeg mpg mpe m2v," +
     3410                "video/quicktime,qt mov," +
     3411                "video/mp4,mp4," +
     3412                "video/x-m4v,m4v," +
     3413                "video/x-flv,flv," +
     3414                "video/x-ms-wmv,wmv," +
     3415                "video/avi,avi," +
     3416                "video/webm,webm," +
     3417                "video/3gpp,3gpp 3gp," +
     3418                "video/3gpp2,3g2," +
     3419                "video/vnd.rn-realvideo,rv," +
     3420                "video/ogg,ogv," +
     3421                "video/x-matroska,mkv," +
     3422                "application/vnd.oasis.opendocument.formula-template,otf," +
     3423                "application/octet-stream,exe";
     3424
     3425
     3426        /**
     3427         * Map of mimes to extensions
     3428         *
     3429         * @property mimes
     3430         * @type {Object}
     3431         */
     3432        var mimes = {};
     3433
     3434        /**
     3435         * Map of extensions to mimes
     3436         *
     3437         * @property extensions
     3438         * @type {Object}
     3439         */
     3440        var extensions = {};
     3441
     3442
     3443        /**
     3444        * Parses mimeData string into a mimes and extensions lookup maps. String should have the
     3445        * following format:
     3446        *
     3447        * application/msword,doc dot,application/pdf,pdf, ...
     3448        *
     3449        * so mime-type followed by comma and followed by space-separated list of associated extensions,
     3450        * then comma again and then another mime-type, etc.
     3451        *
     3452        * If invoked externally will replace override internal lookup maps with user-provided data.
     3453        *
     3454        * @method addMimeType
     3455        * @param {String} mimeData
     3456        */
     3457        var addMimeType = function (mimeData) {
     3458                var items = mimeData.split(/,/), i, ii, ext;
     3459
     3460                for (i = 0; i < items.length; i += 2) {
     3461                        ext = items[i + 1].split(/ /);
     3462
     3463                        // extension to mime lookup
     3464                        for (ii = 0; ii < ext.length; ii++) {
     3465                                mimes[ext[ii]] = items[i];
     3466                        }
     3467                        // mime to extension lookup
     3468                        extensions[items[i]] = ext;
     3469                }
     3470        };
     3471
     3472
     3473        var extList2mimes = function (filters, addMissingExtensions) {
     3474                var ext, i, ii, type, mimes = [];
     3475
     3476                // convert extensions to mime types list
     3477                for (i = 0; i < filters.length; i++) {
     3478                        ext = filters[i].extensions.toLowerCase().split(/\s*,\s*/);
     3479
     3480                        for (ii = 0; ii < ext.length; ii++) {
     3481
     3482                                // if there's an asterisk in the list, then accept attribute is not required
     3483                                if (ext[ii] === '*') {
     3484                                        return [];
     3485                                }
     3486
     3487                                type = mimes[ext[ii]];
     3488
     3489                                // future browsers should filter by extension, finally
     3490                                if (addMissingExtensions && /^\w+$/.test(ext[ii])) {
     3491                                        mimes.push('.' + ext[ii]);
     3492                                } else if (type && Basic.inArray(type, mimes) === -1) {
     3493                                        mimes.push(type);
     3494                                } else if (!type) {
     3495                                        // if we have no type in our map, then accept all
     3496                                        return [];
     3497                                }
     3498                        }
     3499                }
     3500                return mimes;
     3501        };
     3502
     3503
     3504        var mimes2exts = function(mimes) {
     3505                var exts = [];
     3506
     3507                Basic.each(mimes, function(mime) {
     3508                        mime = mime.toLowerCase();
     3509
     3510                        if (mime === '*') {
     3511                                exts = [];
     3512                                return false;
     3513                        }
     3514
     3515                        // check if this thing looks like mime type
     3516                        var m = mime.match(/^(\w+)\/(\*|\w+)$/);
     3517                        if (m) {
     3518                                if (m[2] === '*') {
     3519                                        // wildcard mime type detected
     3520                                        Basic.each(extensions, function(arr, mime) {
     3521                                                if ((new RegExp('^' + m[1] + '/')).test(mime)) {
     3522                                                        [].push.apply(exts, extensions[mime]);
     3523                                                }
     3524                                        });
     3525                                } else if (extensions[mime]) {
     3526                                        [].push.apply(exts, extensions[mime]);
     3527                                }
     3528                        }
    28803529                });
     3530                return exts;
    28813531        };
    28823532
    28833533
     3534        var mimes2extList = function(mimes) {
     3535                var accept = [], exts = [];
     3536
     3537                if (Basic.typeOf(mimes) === 'string') {
     3538                        mimes = Basic.trim(mimes).split(/\s*,\s*/);
     3539                }
     3540
     3541                exts = mimes2exts(mimes);
     3542
     3543                accept.push({
     3544                        title: I18n.translate('Files'),
     3545                        extensions: exts.length ? exts.join(',') : '*'
     3546                });
     3547
     3548                return accept;
     3549        };
     3550
     3551        /**
     3552         * Extract extension from the given filename
     3553         *
     3554         * @method getFileExtension
     3555         * @param {String} fileName
     3556         * @return {String} File extension
     3557         */
     3558        var getFileExtension = function(fileName) {
     3559                var matches = fileName && fileName.match(/\.([^.]+)$/);
     3560                if (matches) {
     3561                        return matches[1].toLowerCase();
     3562                }
     3563                return '';
     3564        };
     3565
     3566
     3567        /**
     3568         * Get file mime-type from it's filename - will try to match the extension
     3569         * against internal mime-type lookup map
     3570         *
     3571         * @method getFileMime
     3572         * @param {String} fileName
     3573         * @return File mime-type if found or an empty string if not
     3574         */
     3575        var getFileMime = function(fileName) {
     3576                return mimes[getFileExtension(fileName)] || '';
     3577        };
     3578
     3579
     3580        addMimeType(mimeData);
     3581
     3582        return {
     3583                mimes: mimes,
     3584                extensions: extensions,
     3585                addMimeType: addMimeType,
     3586                extList2mimes: extList2mimes,
     3587                mimes2exts: mimes2exts,
     3588                mimes2extList: mimes2extList,
     3589                getFileExtension: getFileExtension,
     3590                getFileMime: getFileMime
     3591        }
    28843592});
    28853593
    28863594// Included from: src/javascript/file/FileInput.js
     
    29113619        converts selected files to _File_ objects, to be used in conjunction with _Image_, preloaded in memory
    29123620        with _FileReader_ or uploaded to a server through _XMLHttpRequest_.
    29133621
    2914         @class FileInput
     3622        @class moxie/file/FileInput
    29153623        @constructor
    29163624        @extends EventTarget
    29173625        @uses RuntimeClient
     
    29183626        @param {Object|String|DOMElement} options If options is string or node, argument is considered as _browse\_button_.
    29193627                @param {String|DOMElement} options.browse_button DOM Element to turn into file picker.
    29203628                @param {Array} [options.accept] Array of mime types to accept. By default accepts all.
    2921                 @param {String} [options.file='file'] Name of the file field (not the filename).
    29223629                @param {Boolean} [options.multiple=false] Enable selection of multiple files.
    29233630                @param {Boolean} [options.directory=false] Turn file input into the folder input (cannot be both at the same time).
    2924                 @param {String|DOMElement} [options.container] DOM Element to use as a container for file-picker. Defaults to parentNode 
     3631                @param {String|DOMElement} [options.container] DOM Element to use as a container for file-picker. Defaults to parentNode
    29253632                for _browse\_button_.
    29263633                @param {Object|String} [options.required_caps] Set of required capabilities, that chosen runtime must support.
    29273634
     
    29313638                </div>
    29323639
    29333640                <script>
    2934                         var fileInput = new mOxie.FileInput({
     3641                        var fileInput = new moxie.file.FileInput({
    29353642                                browse_button: 'file-picker', // or document.getElementById('file-picker')
    29363643                                container: 'container',
    29373644                                accept: [
     
    29583665                'ready',
    29593666
    29603667                /**
    2961                 Dispatched right after [ready](#event_ready) event, and whenever [refresh()](#method_refresh) is invoked. 
     3668                Dispatched right after [ready](#event_ready) event, and whenever [refresh()](#method_refresh) is invoked.
    29623669                Check [corresponding documentation entry](#method_refresh) for more info.
    29633670
    29643671                @event refresh
     
    30123719
    30133720        function FileInput(options) {
    30143721                if (MXI_DEBUG) {
    3015                         Env.log("Instantiating FileInput..."); 
     3722                        Env.log("Instantiating FileInput...");
    30163723                }
    30173724
    3018                 var self = this,
    3019                         container, browseButton, defaults;
     3725                var container, browseButton, defaults;
    30203726
    30213727                // if flat argument passed it should be browse_button id
    30223728                if (Basic.inArray(Basic.typeOf(options), ['string', 'node']) !== -1) {
     
    30363742                                title: I18n.translate('All Files'),
    30373743                                extensions: '*'
    30383744                        }],
    3039                         name: 'file',
    30403745                        multiple: false,
    30413746                        required_caps: false,
    30423747                        container: browseButton.parentNode || document.body
    30433748                };
    3044                
     3749
    30453750                options = Basic.extend({}, defaults, options);
    30463751
    30473752                // convert to object representation
     
    30483753                if (typeof(options.required_caps) === 'string') {
    30493754                        options.required_caps = Runtime.parseCaps(options.required_caps);
    30503755                }
    3051                                        
     3756
    30523757                // normalize accept option (could be list of mime types or array of title/extensions pairs)
    30533758                if (typeof(options.accept) === 'string') {
    30543759                        options.accept = Mime.mimes2extList(options.accept);
     
    30663771                }
    30673772
    30683773                container = browseButton = null; // IE
    3069                                                
    3070                 RuntimeClient.call(self);
    3071                
    3072                 Basic.extend(self, {
     3774
     3775                RuntimeClient.call(this);
     3776
     3777                Basic.extend(this, {
    30733778                        /**
    30743779                        Unique id of the component
    30753780
     
    30803785                        @default UID
    30813786                        */
    30823787                        uid: Basic.guid('uid_'),
    3083                        
     3788
    30843789                        /**
    30853790                        Unique id of the connected runtime, if any.
    30863791
     
    30983803                        @type {String}
    30993804                        */
    31003805                        shimid: null,
    3101                        
     3806
    31023807                        /**
    3103                         Array of selected mOxie.File objects
     3808                        Array of selected moxie.file.File objects
    31043809
    31053810                        @property files
    31063811                        @type {Array}
     
    31143819                        @method init
    31153820                        */
    31163821                        init: function() {
     3822                                var self = this;
     3823
    31173824                                self.bind('RuntimeInit', function(e, runtime) {
    31183825                                        self.ruid = runtime.uid;
    31193826                                        self.shimid = runtime.shimid;
     
    31243831
    31253832                                        // re-position and resize shim container
    31263833                                        self.bind('Refresh', function() {
    3127                                                 var pos, size, browseButton, shimContainer;
    3128                                                
     3834                                                var pos, size, browseButton, shimContainer, zIndex;
     3835
    31293836                                                browseButton = Dom.get(options.browse_button);
    31303837                                                shimContainer = Dom.get(runtime.shimid); // do not use runtime.getShimContainer(), since it will create container if it doesn't exist
    31313838
     
    31323839                                                if (browseButton) {
    31333840                                                        pos = Dom.getPos(browseButton, Dom.get(options.container));
    31343841                                                        size = Dom.getSize(browseButton);
     3842                                                        zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 0;
    31353843
    31363844                                                        if (shimContainer) {
    31373845                                                                Basic.extend(shimContainer.style, {
    3138                                                                         top     : pos.y + 'px',
    3139                                                                         left    : pos.x + 'px',
    3140                                                                         width   : size.w + 'px',
    3141                                                                         height  : size.h + 'px'
     3846                                                                        top: pos.y + 'px',
     3847                                                                        left: pos.x + 'px',
     3848                                                                        width: size.w + 'px',
     3849                                                                        height: size.h + 'px',
     3850                                                                        zIndex: zIndex + 1
    31423851                                                                });
    31433852                                                        }
    31443853                                                }
    31453854                                                shimContainer = browseButton = null;
    31463855                                        });
    3147                                        
     3856
    31483857                                        runtime.exec.call(self, 'FileInput', 'init', options);
    31493858                                });
    31503859
     
    31563865                                }));
    31573866                        },
    31583867
     3868
    31593869                        /**
     3870                         * Get current option value by its name
     3871                         *
     3872                         * @method getOption
     3873                         * @param name
     3874                         * @return {Mixed}
     3875                         */
     3876                        getOption: function(name) {
     3877                                return options[name];
     3878                        },
     3879
     3880
     3881                        /**
     3882                         * Sets a new value for the option specified by name
     3883                         *
     3884                         * @method setOption
     3885                         * @param name
     3886                         * @param value
     3887                         */
     3888                        setOption: function(name, value) {
     3889                                if (!options.hasOwnProperty(name)) {
     3890                                        return;
     3891                                }
     3892
     3893                                var oldValue = options[name];
     3894
     3895                                switch (name) {
     3896                                        case 'accept':
     3897                                                if (typeof(value) === 'string') {
     3898                                                        value = Mime.mimes2extList(value);
     3899                                                }
     3900                                                break;
     3901
     3902                                        case 'container':
     3903                                        case 'required_caps':
     3904                                                throw new x.FileException(x.FileException.NO_MODIFICATION_ALLOWED_ERR);
     3905                                }
     3906
     3907                                options[name] = value;
     3908                                this.exec('FileInput', 'setOption', name, value);
     3909
     3910                                this.trigger('OptionChanged', name, value, oldValue);
     3911                        },
     3912
     3913                        /**
    31603914                        Disables file-picker element, so that it doesn't react to mouse clicks.
    31613915
    31623916                        @method disable
     
    31653919                        disable: function(state) {
    31663920                                var runtime = this.getRuntime();
    31673921                                if (runtime) {
    3168                                         runtime.exec.call(this, 'FileInput', 'disable', Basic.typeOf(state) === 'undefined' ? true : state);
     3922                                        this.exec('FileInput', 'disable', Basic.typeOf(state) === 'undefined' ? true : state);
    31693923                                }
    31703924                        },
    31713925
     
    31763930                        @method refresh
    31773931                        */
    31783932                        refresh: function() {
    3179                                 self.trigger("Refresh");
     3933                                this.trigger("Refresh");
    31803934                        },
    31813935
    31823936
     
    31973951                                        Basic.each(this.files, function(file) {
    31983952                                                file.destroy();
    31993953                                        });
    3200                                 } 
     3954                                }
    32013955                                this.files = null;
    32023956
    32033957                                this.unbindAll();
     
    32123966        return FileInput;
    32133967});
    32143968
    3215 // Included from: src/javascript/core/utils/Encode.js
    3216 
    3217 /**
    3218  * Encode.js
    3219  *
    3220  * Copyright 2013, Moxiecode Systems AB
    3221  * Released under GPL License.
    3222  *
    3223  * License: http://www.plupload.com/license
    3224  * Contributing: http://www.plupload.com/contributing
    3225  */
    3226 
    3227 define('moxie/core/utils/Encode', [], function() {
    3228 
    3229         /**
    3230         Encode string with UTF-8
    3231 
    3232         @method utf8_encode
    3233         @for Utils
    3234         @static
    3235         @param {String} str String to encode
    3236         @return {String} UTF-8 encoded string
    3237         */
    3238         var utf8_encode = function(str) {
    3239                 return unescape(encodeURIComponent(str));
    3240         };
    3241        
    3242         /**
    3243         Decode UTF-8 encoded string
    3244 
    3245         @method utf8_decode
    3246         @static
    3247         @param {String} str String to decode
    3248         @return {String} Decoded string
    3249         */
    3250         var utf8_decode = function(str_data) {
    3251                 return decodeURIComponent(escape(str_data));
    3252         };
    3253        
    3254         /**
    3255         Decode Base64 encoded string (uses browser's default method if available),
    3256         from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_decode.js
    3257 
    3258         @method atob
    3259         @static
    3260         @param {String} data String to decode
    3261         @return {String} Decoded string
    3262         */
    3263         var atob = function(data, utf8) {
    3264                 if (typeof(window.atob) === 'function') {
    3265                         return utf8 ? utf8_decode(window.atob(data)) : window.atob(data);
    3266                 }
    3267 
    3268                 // http://kevin.vanzonneveld.net
    3269                 // +   original by: Tyler Akins (http://rumkin.com)
    3270                 // +   improved by: Thunder.m
    3271                 // +      input by: Aman Gupta
    3272                 // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    3273                 // +   bugfixed by: Onno Marsman
    3274                 // +   bugfixed by: Pellentesque Malesuada
    3275                 // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    3276                 // +      input by: Brett Zamir (http://brett-zamir.me)
    3277                 // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    3278                 // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
    3279                 // *     returns 1: 'Kevin van Zonneveld'
    3280                 // mozilla has this native
    3281                 // - but breaks in 2.0.0.12!
    3282                 //if (typeof this.window.atob == 'function') {
    3283                 //    return atob(data);
    3284                 //}
    3285                 var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    3286                 var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
    3287                         ac = 0,
    3288                         dec = "",
    3289                         tmp_arr = [];
    3290 
    3291                 if (!data) {
    3292                         return data;
    3293                 }
    3294 
    3295                 data += '';
    3296 
    3297                 do { // unpack four hexets into three octets using index points in b64
    3298                         h1 = b64.indexOf(data.charAt(i++));
    3299                         h2 = b64.indexOf(data.charAt(i++));
    3300                         h3 = b64.indexOf(data.charAt(i++));
    3301                         h4 = b64.indexOf(data.charAt(i++));
    3302 
    3303                         bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
    3304 
    3305                         o1 = bits >> 16 & 0xff;
    3306                         o2 = bits >> 8 & 0xff;
    3307                         o3 = bits & 0xff;
    3308 
    3309                         if (h3 == 64) {
    3310                                 tmp_arr[ac++] = String.fromCharCode(o1);
    3311                         } else if (h4 == 64) {
    3312                                 tmp_arr[ac++] = String.fromCharCode(o1, o2);
    3313                         } else {
    3314                                 tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
    3315                         }
    3316                 } while (i < data.length);
    3317 
    3318                 dec = tmp_arr.join('');
    3319 
    3320                 return utf8 ? utf8_decode(dec) : dec;
    3321         };
    3322        
    3323         /**
    3324         Base64 encode string (uses browser's default method if available),
    3325         from: https://raw.github.com/kvz/phpjs/master/functions/url/base64_encode.js
    3326 
    3327         @method btoa
    3328         @static
    3329         @param {String} data String to encode
    3330         @return {String} Base64 encoded string
    3331         */
    3332         var btoa = function(data, utf8) {
    3333                 if (utf8) {
    3334                         data = utf8_encode(data);
    3335                 }
    3336 
    3337                 if (typeof(window.btoa) === 'function') {
    3338                         return window.btoa(data);
    3339                 }
    3340 
    3341                 // http://kevin.vanzonneveld.net
    3342                 // +   original by: Tyler Akins (http://rumkin.com)
    3343                 // +   improved by: Bayron Guevara
    3344                 // +   improved by: Thunder.m
    3345                 // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    3346                 // +   bugfixed by: Pellentesque Malesuada
    3347                 // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    3348                 // +   improved by: Rafał Kukawski (http://kukawski.pl)
    3349                 // *     example 1: base64_encode('Kevin van Zonneveld');
    3350                 // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
    3351                 // mozilla has this native
    3352                 // - but breaks in 2.0.0.12!
    3353                 var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    3354                 var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
    3355                         ac = 0,
    3356                         enc = "",
    3357                         tmp_arr = [];
    3358 
    3359                 if (!data) {
    3360                         return data;
    3361                 }
    3362 
    3363                 do { // pack three octets into four hexets
    3364                         o1 = data.charCodeAt(i++);
    3365                         o2 = data.charCodeAt(i++);
    3366                         o3 = data.charCodeAt(i++);
    3367 
    3368                         bits = o1 << 16 | o2 << 8 | o3;
    3369 
    3370                         h1 = bits >> 18 & 0x3f;
    3371                         h2 = bits >> 12 & 0x3f;
    3372                         h3 = bits >> 6 & 0x3f;
    3373                         h4 = bits & 0x3f;
    3374 
    3375                         // use hexets to index into b64, and append result to encoded string
    3376                         tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    3377                 } while (i < data.length);
    3378 
    3379                 enc = tmp_arr.join('');
    3380 
    3381                 var r = data.length % 3;
    3382 
    3383                 return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
    3384         };
    3385 
    3386 
    3387         return {
    3388                 utf8_encode: utf8_encode,
    3389                 utf8_decode: utf8_decode,
    3390                 atob: atob,
    3391                 btoa: btoa
    3392         };
    3393 });
    3394 
    3395 // Included from: src/javascript/file/Blob.js
    3396 
    3397 /**
    3398  * Blob.js
    3399  *
    3400  * Copyright 2013, Moxiecode Systems AB
    3401  * Released under GPL License.
    3402  *
    3403  * License: http://www.plupload.com/license
    3404  * Contributing: http://www.plupload.com/contributing
    3405  */
    3406 
    3407 define('moxie/file/Blob', [
    3408         'moxie/core/utils/Basic',
    3409         'moxie/core/utils/Encode',
    3410         'moxie/runtime/RuntimeClient'
    3411 ], function(Basic, Encode, RuntimeClient) {
    3412        
    3413         var blobpool = {};
    3414 
    3415         /**
    3416         @class Blob
    3417         @constructor
    3418         @param {String} ruid Unique id of the runtime, to which this blob belongs to
    3419         @param {Object} blob Object "Native" blob object, as it is represented in the runtime
    3420         */
    3421         function Blob(ruid, blob) {
    3422 
    3423                 function _sliceDetached(start, end, type) {
    3424                         var blob, data = blobpool[this.uid];
    3425 
    3426                         if (Basic.typeOf(data) !== 'string' || !data.length) {
    3427                                 return null; // or throw exception
    3428                         }
    3429 
    3430                         blob = new Blob(null, {
    3431                                 type: type,
    3432                                 size: end - start
    3433                         });
    3434                         blob.detach(data.substr(start, blob.size));
    3435 
    3436                         return blob;
    3437                 }
    3438 
    3439                 RuntimeClient.call(this);
    3440 
    3441                 if (ruid) {     
    3442                         this.connectRuntime(ruid);
    3443                 }
    3444 
    3445                 if (!blob) {
    3446                         blob = {};
    3447                 } else if (Basic.typeOf(blob) === 'string') { // dataUrl or binary string
    3448                         blob = { data: blob };
    3449                 }
    3450 
    3451                 Basic.extend(this, {
    3452                        
    3453                         /**
    3454                         Unique id of the component
    3455 
    3456                         @property uid
    3457                         @type {String}
    3458                         */
    3459                         uid: blob.uid || Basic.guid('uid_'),
    3460                        
    3461                         /**
    3462                         Unique id of the connected runtime, if falsy, then runtime will have to be initialized
    3463                         before this Blob can be used, modified or sent
    3464 
    3465                         @property ruid
    3466                         @type {String}
    3467                         */
    3468                         ruid: ruid,
    3469        
    3470                         /**
    3471                         Size of blob
    3472 
    3473                         @property size
    3474                         @type {Number}
    3475                         @default 0
    3476                         */
    3477                         size: blob.size || 0,
    3478                        
    3479                         /**
    3480                         Mime type of blob
    3481 
    3482                         @property type
    3483                         @type {String}
    3484                         @default ''
    3485                         */
    3486                         type: blob.type || '',
    3487                        
    3488                         /**
    3489                         @method slice
    3490                         @param {Number} [start=0]
    3491                         */
    3492                         slice: function(start, end, type) {             
    3493                                 if (this.isDetached()) {
    3494                                         return _sliceDetached.apply(this, arguments);
    3495                                 }
    3496                                 return this.getRuntime().exec.call(this, 'Blob', 'slice', this.getSource(), start, end, type);
    3497                         },
    3498 
    3499                         /**
    3500                         Returns "native" blob object (as it is represented in connected runtime) or null if not found
    3501 
    3502                         @method getSource
    3503                         @return {Blob} Returns "native" blob object or null if not found
    3504                         */
    3505                         getSource: function() {
    3506                                 if (!blobpool[this.uid]) {
    3507                                         return null;   
    3508                                 }
    3509                                 return blobpool[this.uid];
    3510                         },
    3511 
    3512                         /**
    3513                         Detaches blob from any runtime that it depends on and initialize with standalone value
    3514 
    3515                         @method detach
    3516                         @protected
    3517                         @param {DOMString} [data=''] Standalone value
    3518                         */
    3519                         detach: function(data) {
    3520                                 if (this.ruid) {
    3521                                         this.getRuntime().exec.call(this, 'Blob', 'destroy');
    3522                                         this.disconnectRuntime();
    3523                                         this.ruid = null;
    3524                                 }
    3525 
    3526                                 data = data || '';
    3527 
    3528                                 // if dataUrl, convert to binary string
    3529                                 if (data.substr(0, 5) == 'data:') {
    3530                                         var base64Offset = data.indexOf(';base64,');
    3531                                         this.type = data.substring(5, base64Offset);
    3532                                         data = Encode.atob(data.substring(base64Offset + 8));
    3533                                 }
    3534 
    3535                                 this.size = data.length;
    3536 
    3537                                 blobpool[this.uid] = data;
    3538                         },
    3539 
    3540                         /**
    3541                         Checks if blob is standalone (detached of any runtime)
    3542                        
    3543                         @method isDetached
    3544                         @protected
    3545                         @return {Boolean}
    3546                         */
    3547                         isDetached: function() {
    3548                                 return !this.ruid && Basic.typeOf(blobpool[this.uid]) === 'string';
    3549                         },
    3550                        
    3551                         /**
    3552                         Destroy Blob and free any resources it was using
    3553 
    3554                         @method destroy
    3555                         */
    3556                         destroy: function() {
    3557                                 this.detach();
    3558                                 delete blobpool[this.uid];
    3559                         }
    3560                 });
    3561 
    3562                
    3563                 if (blob.data) {
    3564                         this.detach(blob.data); // auto-detach if payload has been passed
    3565                 } else {
    3566                         blobpool[this.uid] = blob;     
    3567                 }
    3568         }
    3569        
    3570         return Blob;
    3571 });
    3572 
    35733969// Included from: src/javascript/file/File.js
    35743970
    35753971/**
     
    35883984        'moxie/file/Blob'
    35893985], function(Basic, Mime, Blob) {
    35903986        /**
    3591         @class File
     3987        @class moxie/file/File
    35923988        @extends Blob
    35933989        @constructor
    35943990        @param {String} ruid Unique id of the runtime, to which this blob belongs to
     
    36914087                <div id="filelist"></div>
    36924088
    36934089                <script type="text/javascript">
    3694                         var fileDrop = new mOxie.FileDrop('drop_zone'), fileList = mOxie.get('filelist');
     4090                        var fileDrop = new moxie.file.FileDrop('drop_zone'), fileList = moxie.utils.Dom.get('filelist');
    36954091
    36964092                        fileDrop.ondrop = function() {
    3697                                 mOxie.each(this.files, function(file) {
     4093                                moxie.utils.Basic.each(this.files, function(file) {
    36984094                                        fileList.innerHTML += '<div>' + file.name + '</div>';
    36994095                                });
    37004096                        };
     
    37024098                        fileDrop.init();
    37034099                </script>
    37044100
    3705         @class FileDrop
     4101        @class moxie/file/FileDrop
    37064102        @constructor
    37074103        @extends EventTarget
    37084104        @uses RuntimeClient
     
    38554251        Utility for preloading o.Blob/o.File objects in memory. By design closely follows [W3C FileReader](http://www.w3.org/TR/FileAPI/#dfn-filereader)
    38564252        interface. Where possible uses native FileReader, where - not falls back to shims.
    38574253
    3858         @class FileReader
     4254        @class moxie/file/FileReader
    38594255        @constructor FileReader
    38604256        @extends EventTarget
    38614257        @uses RuntimeClient
     
    41184514 * Contributing: http://www.plupload.com/contributing
    41194515 */
    41204516
    4121 define('moxie/core/utils/Url', [], function() {
     4517/**
     4518@class moxie/core/utils/Url
     4519@public
     4520@static
     4521*/
     4522
     4523define('moxie/core/utils/Url', [
     4524        'moxie/core/utils/Basic'
     4525], function(Basic) {
    41224526        /**
    41234527        Parse url into separate components and fill in absent parts with parts from current url,
    41244528        based on https://raw.github.com/kvz/phpjs/master/functions/url/parse_url.js
    41254529
    41264530        @method parseUrl
    4127         @for Utils
    41284531        @static
    41294532        @param {String} url Url to parse (defaults to empty string if undefined)
    41304533        @return {Object} Hash containing extracted uri components
     
    41374540                        https: 443
    41384541                }
    41394542                , uri = {}
    4140                 , regex = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/
     4543                , regex = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@\/]*):?([^:@\/]*))?@)?(\[[\da-fA-F:]+\]|[^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\\?([^#]*))?(?:#(.*))?)/
    41414544                , m = regex.exec(url || '')
     4545                , isRelative
     4546                , isSchemeLess = /^\/\/\w/.test(url)
    41424547                ;
    4143                                        
     4548
     4549                switch (Basic.typeOf(currentUrl)) {
     4550                        case 'undefined':
     4551                                currentUrl = parseUrl(document.location.href, false);
     4552                                break;
     4553
     4554                        case 'string':
     4555                                currentUrl = parseUrl(currentUrl, false);
     4556                                break;
     4557                }
     4558
    41444559                while (i--) {
    41454560                        if (m[i]) {
    41464561                                uri[key[i]] = m[i];
     
    41474562                        }
    41484563                }
    41494564
    4150                 // when url is relative, we set the origin and the path ourselves
    4151                 if (!uri.scheme) {
    4152                         // come up with defaults
    4153                         if (!currentUrl || typeof(currentUrl) === 'string') {
    4154                                 currentUrl = parseUrl(currentUrl || document.location.href);
    4155                         }
     4565                isRelative = !isSchemeLess && !uri.scheme;
    41564566
     4567                if (isSchemeLess || isRelative) {
    41574568                        uri.scheme = currentUrl.scheme;
     4569                }
     4570
     4571                // when url is relative, we set the origin and the path ourselves
     4572                if (isRelative) {
    41584573                        uri.host = currentUrl.host;
    41594574                        uri.port = currentUrl.port;
    41604575
     
    41754590
    41764591                if (!uri.port) {
    41774592                        uri.port = ports[uri.scheme] || 80;
    4178                 } 
    4179                
     4593                }
     4594
    41804595                uri.port = parseInt(uri.port, 10);
    41814596
    41824597                if (!uri.path) {
     
    42114626        Check if specified url has the same origin as the current document
    42124627
    42134628        @method hasSameOrigin
     4629        @static
    42144630        @param {String|Object} url
    42154631        @return {Boolean}
    42164632        */
     
    42184634                function origin(url) {
    42194635                        return [url.scheme, url.host, url.port].join('/');
    42204636                }
    4221                        
     4637
    42224638                if (typeof url === 'string') {
    42234639                        url = parseUrl(url);
    4224                 }       
    4225                
     4640                }
     4641
    42264642                return origin(parseUrl()) === origin(url);
    42274643        };
    42284644
     
    42544670        Instance of this class can be used as a target for the events dispatched by shims,
    42554671        when allowing them onto components is for either reason inappropriate
    42564672
    4257         @class RuntimeTarget
     4673        @class moxie/runtime/RuntimeTarget
    42584674        @constructor
    42594675        @protected
    42604676        @extends EventTarget
     
    42974713        it can be used to read only preloaded blobs/files and only below certain size (not yet sure what that'd be,
    42984714        but probably < 1mb). Not meant to be used directly by user.
    42994715
    4300         @class FileReaderSync
     4716        @class moxie/file/FileReaderSync
    43014717        @private
    43024718        @constructor
    43034719        */
     
    43684784        /**
    43694785        FormData
    43704786
    4371         @class FormData
     4787        @class moxie/xhr/FormData
    43724788        @constructor
    43734789        */
    43744790        function FormData() {
     
    45554971        function XMLHttpRequestUpload() {
    45564972                this.uid = Basic.guid('uid_');
    45574973        }
    4558        
     4974
    45594975        XMLHttpRequestUpload.prototype = EventTarget.instance;
    45604976
    45614977        /**
    45624978        Implementation of XMLHttpRequest
    45634979
    4564         @class XMLHttpRequest
     4980        @class moxie/xhr/XMLHttpRequest
    45654981        @constructor
    45664982        @uses RuntimeClient
    45674983        @extends EventTarget
     
    45824998                'loadend'
    45834999
    45845000                // readystatechange (for historical reasons)
    4585         ]; 
    4586        
     5001        ];
     5002
    45875003        var NATIVE = 1, RUNTIME = 2;
    4588                                        
     5004
    45895005        function XMLHttpRequest() {
    45905006                var self = this,
    45915007                        // this (together with _p() @see below) is here to gracefully upgrade to setter/getter syntax where possible
     
    46515067                                /**
    46525068                                Returns the response type. Can be set to change the response type. Values are:
    46535069                                the empty string (default), "arraybuffer", "blob", "document", "json", and "text".
    4654                                
     5070
    46555071                                @property responseType
    46565072                                @type String
    46575073                                */
     
    46595075
    46605076                                /**
    46615077                                Returns the document response entity body.
    4662                                
     5078
    46635079                                Throws an "InvalidStateError" exception if responseType is not the empty string or "document".
    46645080
    46655081                                @property responseXML
     
    46695085
    46705086                                /**
    46715087                                Returns the text response entity body.
    4672                                
     5088
    46735089                                Throws an "InvalidStateError" exception if responseType is not the empty string or "text".
    46745090
    46755091                                @property responseText
     
    46805096                                /**
    46815097                                Returns the response entity body (http://www.w3.org/TR/XMLHttpRequest/#response-entity-body).
    46825098                                Can become: ArrayBuffer, Blob, Document, JSON, Text
    4683                                
     5099
    46845100                                @property response
    46855101                                @type Mixed
    46865102                                */
     
    47175133                        _responseHeadersBag
    47185134                        ;
    47195135
    4720                
     5136
    47215137                Basic.extend(this, props, {
    47225138                        /**
    47235139                        Unique id of the component
     
    47265142                        @type String
    47275143                        */
    47285144                        uid: Basic.guid('uid_'),
    4729                        
     5145
    47305146                        /**
    47315147                        Target for Upload events
    47325148
     
    47345150                        @type XMLHttpRequestUpload
    47355151                        */
    47365152                        upload: new XMLHttpRequestUpload(),
    4737                        
    47385153
     5154
    47395155                        /**
    47405156                        Sets the request method, request URL, synchronous flag, request username, and request password.
    47415157
     
    47625178                        */
    47635179                        open: function(method, url, async, user, password) {
    47645180                                var urlp;
    4765                                
     5181
    47665182                                // first two arguments are required
    47675183                                if (!method || !url) {
    47685184                                        throw new x.DOMException(x.DOMException.SYNTAX_ERR);
    47695185                                }
    4770                                
     5186
    47715187                                // 2 - check if any code point in method is higher than U+00FF or after deflating method it does not match the method
    47725188                                if (/[\u0100-\uffff]/.test(method) || Encode.utf8_encode(method) !== method) {
    47735189                                        throw new x.DOMException(x.DOMException.SYNTAX_ERR);
     
    47775193                                if (!!~Basic.inArray(method.toUpperCase(), ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'TRACE', 'TRACK'])) {
    47785194                                        _method = method.toUpperCase();
    47795195                                }
    4780                                
    4781                                
     5196
     5197
    47825198                                // 4 - allowing these methods poses a security risk
    47835199                                if (!!~Basic.inArray(_method, ['CONNECT', 'TRACE', 'TRACK'])) {
    47845200                                        throw new x.DOMException(x.DOMException.SECURITY_ERR);
     
    47865202
    47875203                                // 5
    47885204                                url = Encode.utf8_encode(url);
    4789                                
     5205
    47905206                                // 6 - Resolve url relative to the XMLHttpRequest base URL. If the algorithm returns an error, throw a "SyntaxError".
    47915207                                urlp = Url.parseUrl(url);
    47925208
    47935209                                _same_origin_flag = Url.hasSameOrigin(urlp);
    4794                                                                                                                                
     5210
    47955211                                // 7 - manually build up absolute url
    47965212                                _url = Url.resolveUrl(url);
    4797                
     5213
    47985214                                // 9-10, 12-13
    47995215                                if ((user || password) && !_same_origin_flag) {
    48005216                                        throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
     
    48025218
    48035219                                _user = user || urlp.user;
    48045220                                _password = password || urlp.pass;
    4805                                
     5221
    48065222                                // 11
    48075223                                _async = async || true;
    4808                                
     5224
    48095225                                if (_async === false && (_p('timeout') || _p('withCredentials') || _p('responseType') !== "")) {
    48105226                                        throw new x.DOMException(x.DOMException.INVALID_ACCESS_ERR);
    48115227                                }
    4812                                
     5228
    48135229                                // 14 - terminate abort()
    4814                                
     5230
    48155231                                // 15 - terminate send()
    48165232
    48175233                                // 18
     
    48225238
    48235239                                // 19
    48245240                                _p('readyState', XMLHttpRequest.OPENED);
    4825                                
     5241
    48265242                                // 20
    48275243                                this.dispatchEvent('readystatechange');
    48285244                        },
    4829                        
     5245
    48305246                        /**
    48315247                        Appends an header to the list of author request headers, or if header is already
    48325248                        in the list of author request headers, combines its value with value.
     
    48345250                        Throws an "InvalidStateError" exception if the state is not OPENED or if the send() flag is set.
    48355251                        Throws a "SyntaxError" exception if header is not a valid HTTP header field name or if value
    48365252                        is not a valid HTTP header field value.
    4837                        
     5253
    48385254                        @method setRequestHeader
    48395255                        @param {String} header
    48405256                        @param {String|Number} value
     
    48635279                                                "user-agent",
    48645280                                                "via"
    48655281                                        ];
    4866                                
     5282
    48675283                                // 1-2
    48685284                                if (_p('readyState') !== XMLHttpRequest.OPENED || _send_flag) {
    48695285                                        throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
     
    48815297                                }*/
    48825298
    48835299                                header = Basic.trim(header).toLowerCase();
    4884                                
     5300
    48855301                                // setting of proxy-* and sec-* headers is prohibited by spec
    48865302                                if (!!~Basic.inArray(header, uaHeaders) || /^(proxy\-|sec\-)/.test(header)) {
    48875303                                        return false;
     
    48905306                                // camelize
    48915307                                // browsers lowercase header names (at least for custom ones)
    48925308                                // header = header.replace(/\b\w/g, function($1) { return $1.toUpperCase(); });
    4893                                
     5309
    48945310                                if (!_headers[header]) {
    48955311                                        _headers[header] = value;
    48965312                                } else {
     
    49015317                        },
    49025318
    49035319                        /**
     5320                         * Test if the specified header is already set on this request.
     5321                         * Returns a header value or boolean false if it's not yet set.
     5322                         *
     5323                         * @method hasRequestHeader
     5324                         * @param {String} header Name of the header to test
     5325                         * @return {Boolean|String}
     5326                         */
     5327                        hasRequestHeader: function(header) {
     5328                                return header && _headers[header.toLowerCase()] || false;
     5329                        },
     5330
     5331                        /**
    49045332                        Returns all headers from the response, with the exception of those whose field name is Set-Cookie or Set-Cookie2.
    49055333
    49065334                        @method getAllResponseHeaders
     
    49115339                        },
    49125340
    49135341                        /**
    4914                         Returns the header field value from the response of which the field name matches header, 
     5342                        Returns the header field value from the response of which the field name matches header,
    49155343                        unless the field name is Set-Cookie or Set-Cookie2.
    49165344
    49175345                        @method getResponseHeader
     
    49465374                                }
    49475375                                return null;
    49485376                        },
    4949                        
     5377
    49505378                        /**
    49515379                        Sets the Content-Type header for the response to mime.
    49525380                        Throws an "InvalidStateError" exception if the state is LOADING or DONE.
     
    49575385                        */
    49585386                        overrideMimeType: function(mime) {
    49595387                                var matches, charset;
    4960                        
     5388
    49615389                                // 1
    49625390                                if (!!~Basic.inArray(_p('readyState'), [XMLHttpRequest.LOADING, XMLHttpRequest.DONE])) {
    49635391                                        throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
     
    49815409                                _finalMime = mime;
    49825410                                _finalCharset = charset;
    49835411                        },
    4984                        
     5412
    49855413                        /**
    49865414                        Initiates the request. The optional argument provides the request entity body.
    49875415                        The argument is ignored if request method is GET or HEAD.
     
    49925420                        @param {Blob|Document|String|FormData} [data] Request entity body
    49935421                        @param {Object} [options] Set of requirements and pre-requisities for runtime initialization
    49945422                        */
    4995                         send: function(data, options) {                                 
     5423                        send: function(data, options) {
    49965424                                if (Basic.typeOf(options) === 'string') {
    49975425                                        _options = { ruid: options };
    49985426                                } else if (!options) {
     
    50005428                                } else {
    50015429                                        _options = options;
    50025430                                }
    5003                                                                                                                        
     5431
    50045432                                // 1-2
    50055433                                if (this.readyState !== XMLHttpRequest.OPENED || _send_flag) {
    50065434                                        throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
    50075435                                }
    5008                                
    5009                                 // 3                                   
     5436
     5437                                // 3
    50105438                                // sending Blob
    50115439                                if (data instanceof Blob) {
    50125440                                        _options.ruid = data.ruid;
    50135441                                        _mimeType = data.type || 'application/octet-stream';
    50145442                                }
    5015                                
     5443
    50165444                                // FormData
    50175445                                else if (data instanceof FormData) {
    50185446                                        if (data.hasBlob()) {
     
    50215449                                                _mimeType = blob.type || 'application/octet-stream';
    50225450                                        }
    50235451                                }
    5024                                
     5452
    50255453                                // DOMString
    50265454                                else if (typeof data === 'string') {
    50275455                                        _encoding = 'UTF-8';
    50285456                                        _mimeType = 'text/plain;charset=UTF-8';
    5029                                        
     5457
    50305458                                        // data should be converted to Unicode and encoded as UTF-8
    50315459                                        data = Encode.utf8_encode(data);
    50325460                                }
     
    50575485                                // 8.5 - Return the send() method call, but continue running the steps in this algorithm.
    50585486                                _doXHR.call(this, data);
    50595487                        },
    5060                        
     5488
    50615489                        /**
    50625490                        Cancels any network activity.
    5063                        
     5491
    50645492                        @method abort
    50655493                        */
    50665494                        abort: function() {
     
    52675695                                }
    52685696                        }
    52695697                }
    5270                
     5698
    52715699                /*
    52725700                function _toASCII(str, AllowUnassigned, UseSTD3ASCIIRules) {
    52735701                        // TODO: http://tools.ietf.org/html/rfc3490#section-4.1
     
    52745702                        return str.toLowerCase();
    52755703                }
    52765704                */
    5277                
    5278                
     5705
     5706
    52795707                function _doXHR(data) {
    52805708                        var self = this;
    5281                        
     5709
    52825710                        _start_time = new Date().getTime();
    52835711
    52845712                        _xhr = new RuntimeTarget();
     
    52985726                                        self.dispatchEvent('readystatechange');
    52995727
    53005728                                        self.dispatchEvent(e);
    5301                                        
     5729
    53025730                                        if (_upload_events_flag) {
    53035731                                                self.upload.dispatchEvent(e);
    53045732                                        }
    53055733                                });
    5306                                
     5734
    53075735                                _xhr.bind('Progress', function(e) {
    53085736                                        if (_p('readyState') !== XMLHttpRequest.LOADING) {
    53095737                                                _p('readyState', XMLHttpRequest.LOADING); // LoadStart unreliable (in Flash for example)
     
    53115739                                        }
    53125740                                        self.dispatchEvent(e);
    53135741                                });
    5314                                
     5742
    53155743                                _xhr.bind('UploadProgress', function(e) {
    53165744                                        if (_upload_events_flag) {
    53175745                                                self.upload.dispatchEvent({
     
    53225750                                                });
    53235751                                        }
    53245752                                });
    5325                                
     5753
    53265754                                _xhr.bind('Load', function(e) {
    53275755                                        _p('readyState', XMLHttpRequest.DONE);
    53285756                                        _p('status', Number(runtime.exec.call(_xhr, 'XMLHttpRequest', 'getStatus') || 0));
    53295757                                        _p('statusText', httpCode[_p('status')] || "");
    5330                                        
     5758
    53315759                                        _p('response', runtime.exec.call(_xhr, 'XMLHttpRequest', 'getResponse', _p('responseType')));
    53325760
    53335761                                        if (!!~Basic.inArray(_p('responseType'), ['text', ''])) {
     
    53395767                                        _responseHeaders = runtime.exec.call(_xhr, 'XMLHttpRequest', 'getAllResponseHeaders');
    53405768
    53415769                                        self.dispatchEvent('readystatechange');
    5342                                        
     5770
    53435771                                        if (_p('status') > 0) { // status 0 usually means that server is unreachable
    53445772                                                if (_upload_events_flag) {
    53455773                                                        self.upload.dispatchEvent(e);
     
    53565784                                        self.dispatchEvent(e);
    53575785                                        loadEnd();
    53585786                                });
    5359                                
     5787
    53605788                                _xhr.bind('Error', function(e) {
    53615789                                        _error_flag = true;
    53625790                                        _p('readyState', XMLHttpRequest.DONE);
     
    54015829                        if (!_same_origin_flag) {
    54025830                                _options.required_caps.do_cors = true;
    54035831                        }
    5404                        
    54055832
     5833
    54065834                        if (_options.ruid) { // we do not need to wait if we can connect directly
    54075835                                exec(_xhr.connectRuntime(_options));
    54085836                        } else {
     
    54155843                                _xhr.connectRuntime(_options);
    54165844                        }
    54175845                }
    5418        
    5419                
     5846
     5847
    54205848                function _reset() {
    54215849                        _p('responseText', "");
    54225850                        _p('responseXML', null);
     
    54325860        XMLHttpRequest.HEADERS_RECEIVED = 2;
    54335861        XMLHttpRequest.LOADING = 3;
    54345862        XMLHttpRequest.DONE = 4;
    5435        
     5863
    54365864        XMLHttpRequest.prototype = EventTarget.instance;
    54375865
    54385866        return XMLHttpRequest;
     
    54565884        "moxie/runtime/RuntimeClient",
    54575885        "moxie/core/EventTarget"
    54585886], function(Basic, Encode, RuntimeClient, EventTarget) {
     5887
     5888        /**
     5889        @class moxie/runtime/Transporter
     5890        @private
     5891        @constructor
     5892        */
    54595893        function Transporter() {
    54605894                var mod, _runtime, _data, _size, _pos, _chunk_size;
    54615895
     
    56056039        /**
    56066040        Image preloading and manipulation utility. Additionally it provides access to image meta info (Exif, GPS) and raw binary data.
    56076041
    5608         @class Image
     6042        @class moxie/image/Image
    56096043        @constructor
    56106044        @extends EventTarget
    56116045        */
     
    56246058
    56256059                /**
    56266060                Dispatched when resize operation is complete.
    5627                
     6061
    56286062                @event resize
    56296063                @param {Object} event
    56306064                */
     
    57166150                        meta: {},
    57176151
    57186152                        /**
    5719                         Alias for load method, that takes another mOxie.Image object as a source (see load).
     6153                        Alias for load method, that takes another moxie.image.Image object as a source (see load).
    57206154
    57216155                        @method clone
    57226156                        @param {Image} src Source for the image
     
    57276161                        },
    57286162
    57296163                        /**
    5730                         Loads image from various sources. Currently the source for new image can be: mOxie.Image, mOxie.Blob/mOxie.File,
    5731                         native Blob/File, dataUrl or URL. Depending on the type of the source, arguments - differ. When source is URL,
    5732                         Image will be downloaded from remote destination and loaded in memory.
     6164                        Loads image from various sources. Currently the source for new image can be: moxie.image.Image,
     6165                        moxie.file.Blob/moxie.file.File, native Blob/File, dataUrl or URL. Depending on the type of the
     6166                        source, arguments - differ. When source is URL, Image will be downloaded from remote destination
     6167                        and loaded in memory.
    57336168
    57346169                        @example
    5735                                 var img = new mOxie.Image();
     6170                                var img = new moxie.image.Image();
    57366171                                img.onload = function() {
    57376172                                        var blob = img.getAsBlob();
    5738                                        
    5739                                         var formData = new mOxie.FormData();
     6173
     6174                                        var formData = new moxie.xhr.FormData();
    57406175                                        formData.append('file', blob);
    57416176
    5742                                         var xhr = new mOxie.XMLHttpRequest();
     6177                                        var xhr = new moxie.xhr.XMLHttpRequest();
    57436178                                        xhr.onload = function() {
    57446179                                                // upload complete
    57456180                                        };
     
    57476182                                        xhr.send(formData);
    57486183                                };
    57496184                                img.load("http://www.moxiecode.com/images/mox-logo.jpg"); // notice file extension (.jpg)
    5750                        
    57516185
     6186
    57526187                        @method load
    57536188                        @param {Image|Blob|File|String} src Source for the image
    57546189                        @param {Boolean|Object} [mixed]
     
    57576192                                _load.apply(this, arguments);
    57586193                        },
    57596194
     6195
    57606196                        /**
     6197                        Resizes the image to fit the specified width/height. If crop is specified, image will also be
     6198                        cropped to the exact dimensions.
     6199
     6200                        @method resize
     6201                        @since 3.0
     6202                        @param {Object} options
     6203                                @param {Number} options.width Resulting width
     6204                                @param {Number} [options.height=width] Resulting height (optional, if not supplied will default to width)
     6205                                @param {String} [options.type='image/jpeg'] MIME type of the resulting image
     6206                                @param {Number} [options.quality=90] In the case of JPEG, controls the quality of resulting image
     6207                                @param {Boolean} [options.crop='cc'] If not falsy, image will be cropped, by default from center
     6208                                @param {Boolean} [options.fit=true] Whether to upscale the image to fit the exact dimensions
     6209                                @param {Boolean} [options.preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
     6210                                @param {String} [options.resample='default'] Resampling algorithm to use during resize
     6211                                @param {Boolean} [options.multipass=true] Whether to scale the image in steps (results in better quality)
     6212                        */
     6213                        resize: function(options) {
     6214                                var self = this;
     6215                                var orientation;
     6216                                var scale;
     6217
     6218                                var srcRect = {
     6219                                        x: 0,
     6220                                        y: 0,
     6221                                        width: self.width,
     6222                                        height: self.height
     6223                                };
     6224
     6225                                var opts = Basic.extendIf({
     6226                                        width: self.width,
     6227                                        height: self.height,
     6228                                        type: self.type || 'image/jpeg',
     6229                                        quality: 90,
     6230                                        crop: false,
     6231                                        fit: true,
     6232                                        preserveHeaders: true,
     6233                                        resample: 'default',
     6234                                        multipass: true
     6235                                }, options);
     6236
     6237                                try {
     6238                                        if (!self.size) { // only preloaded image objects can be used as source
     6239                                                throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
     6240                                        }
     6241
     6242                                        // no way to reliably intercept the crash due to high resolution, so we simply avoid it
     6243                                        if (self.width > Image.MAX_RESIZE_WIDTH || self.height > Image.MAX_RESIZE_HEIGHT) {
     6244                                                throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
     6245                                        }
     6246
     6247                                        // take into account orientation tag
     6248                                        orientation = (self.meta && self.meta.tiff && self.meta.tiff.Orientation) || 1;
     6249
     6250                                        if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation
     6251                                                var tmp = opts.width;
     6252                                                opts.width = opts.height;
     6253                                                opts.height = tmp;
     6254                                        }
     6255
     6256                                        if (opts.crop) {
     6257                                                scale = Math.max(opts.width/self.width, opts.height/self.height);
     6258
     6259                                                if (options.fit) {
     6260                                                        // first scale it up or down to fit the original image
     6261                                                        srcRect.width = Math.min(Math.ceil(opts.width/scale), self.width);
     6262                                                        srcRect.height = Math.min(Math.ceil(opts.height/scale), self.height);
     6263
     6264                                                        // recalculate the scale for adapted dimensions
     6265                                                        scale = opts.width/srcRect.width;
     6266                                                } else {
     6267                                                        srcRect.width = Math.min(opts.width, self.width);
     6268                                                        srcRect.height = Math.min(opts.height, self.height);
     6269
     6270                                                        // now we do not need to scale it any further
     6271                                                        scale = 1;
     6272                                                }
     6273
     6274                                                if (typeof(opts.crop) === 'boolean') {
     6275                                                        opts.crop = 'cc';
     6276                                                }
     6277
     6278                                                switch (opts.crop.toLowerCase().replace(/_/, '-')) {
     6279                                                        case 'rb':
     6280                                                        case 'right-bottom':
     6281                                                                srcRect.x = self.width - srcRect.width;
     6282                                                                srcRect.y = self.height - srcRect.height;
     6283                                                                break;
     6284
     6285                                                        case 'cb':
     6286                                                        case 'center-bottom':
     6287                                                                srcRect.x = Math.floor((self.width - srcRect.width) / 2);
     6288                                                                srcRect.y = self.height - srcRect.height;
     6289                                                                break;
     6290
     6291                                                        case 'lb':
     6292                                                        case 'left-bottom':
     6293                                                                srcRect.x = 0;
     6294                                                                srcRect.y = self.height - srcRect.height;
     6295                                                                break;
     6296
     6297                                                        case 'lt':
     6298                                                        case 'left-top':
     6299                                                                srcRect.x = 0;
     6300                                                                srcRect.y = 0;
     6301                                                                break;
     6302
     6303                                                        case 'ct':
     6304                                                        case 'center-top':
     6305                                                                srcRect.x = Math.floor((self.width - srcRect.width) / 2);
     6306                                                                srcRect.y = 0;
     6307                                                                break;
     6308
     6309                                                        case 'rt':
     6310                                                        case 'right-top':
     6311                                                                srcRect.x = self.width - srcRect.width;
     6312                                                                srcRect.y = 0;
     6313                                                                break;
     6314
     6315                                                        case 'rc':
     6316                                                        case 'right-center':
     6317                                                        case 'right-middle':
     6318                                                                srcRect.x = self.width - srcRect.width;
     6319                                                                srcRect.y = Math.floor((self.height - srcRect.height) / 2);
     6320                                                                break;
     6321
     6322
     6323                                                        case 'lc':
     6324                                                        case 'left-center':
     6325                                                        case 'left-middle':
     6326                                                                srcRect.x = 0;
     6327                                                                srcRect.y = Math.floor((self.height - srcRect.height) / 2);
     6328                                                                break;
     6329
     6330                                                        case 'cc':
     6331                                                        case 'center-center':
     6332                                                        case 'center-middle':
     6333                                                        default:
     6334                                                                srcRect.x = Math.floor((self.width - srcRect.width) / 2);
     6335                                                                srcRect.y = Math.floor((self.height - srcRect.height) / 2);
     6336                                                }
     6337
     6338                                                // original image might be smaller than requested crop, so - avoid negative values
     6339                                                srcRect.x = Math.max(srcRect.x, 0);
     6340                                                srcRect.y = Math.max(srcRect.y, 0);
     6341                                        } else {
     6342                                                scale = Math.min(opts.width/self.width, opts.height/self.height);
     6343
     6344                                                // do not upscale if we were asked to not fit it
     6345                                                if (scale > 1 && !opts.fit) {
     6346                                                        scale = 1;
     6347                                                }
     6348                                        }
     6349
     6350                                        this.exec('Image', 'resize', srcRect, scale, opts);
     6351                                } catch(ex) {
     6352                                        // for now simply trigger error event
     6353                                        self.trigger('error', ex.code);
     6354                                }
     6355                        },
     6356
     6357                        /**
    57616358                        Downsizes the image to fit the specified width/height. If crop is supplied, image will be cropped to exact dimensions.
    57626359
    57636360                        @method downsize
    5764                         @param {Object} opts
    5765                                 @param {Number} opts.width Resulting width
    5766                                 @param {Number} [opts.height=width] Resulting height (optional, if not supplied will default to width)
    5767                                 @param {Boolean} [opts.crop=false] Whether to crop the image to exact dimensions
    5768                                 @param {Boolean} [opts.preserveHeaders=true] Whether to preserve meta headers (on JPEGs after resize)
    5769                                 @param {String} [opts.resample=false] Resampling algorithm to use for resizing
     6361                        @deprecated use resize()
    57706362                        */
    5771                         downsize: function(opts) {
     6363                        downsize: function(options) {
    57726364                                var defaults = {
    57736365                                        width: this.width,
    57746366                                        height: this.height,
     
    57756367                                        type: this.type || 'image/jpeg',
    57766368                                        quality: 90,
    57776369                                        crop: false,
     6370                                        fit: false,
    57786371                                        preserveHeaders: true,
    5779                                         resample: false
    5780                                 };
     6372                                        resample: 'default'
     6373                                }, opts;
    57816374
    5782                                 if (typeof(opts) === 'object') {
    5783                                         opts = Basic.extend(defaults, opts);
     6375                                if (typeof(options) === 'object') {
     6376                                        opts = Basic.extend(defaults, options);
    57846377                                } else {
    57856378                                        // for backward compatibility
    57866379                                        opts = Basic.extend(defaults, {
     
    57916384                                        });
    57926385                                }
    57936386
    5794                                 try {
    5795                                         if (!this.size) { // only preloaded image objects can be used as source
    5796                                                 throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
    5797                                         }
    5798 
    5799                                         // no way to reliably intercept the crash due to high resolution, so we simply avoid it
    5800                                         if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
    5801                                                 throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
    5802                                         }
    5803 
    5804                                         this.exec('Image', 'downsize', opts.width, opts.height, opts.crop, opts.preserveHeaders);
    5805                                 } catch(ex) {
    5806                                         // for now simply trigger error event
    5807                                         this.trigger('error', ex.code);
    5808                                 }
     6387                                this.resize(opts);
    58096388                        },
    58106389
    58116390                        /**
    58126391                        Alias for downsize(width, height, true). (see downsize)
    5813                        
     6392
    58146393                        @method crop
    58156394                        @param {Number} width Resulting width
    58166395                        @param {Number} [height=width] Resulting height (optional, if not supplied will default to width)
     
    58246403                                if (!Env.can('create_canvas')) {
    58256404                                        throw new x.RuntimeError(x.RuntimeError.NOT_SUPPORTED_ERR);
    58266405                                }
    5827 
    5828                                 var runtime = this.connectRuntime(this.ruid);
    5829                                 return runtime.exec.call(this, 'Image', 'getAsCanvas');
     6406                                return this.exec('Image', 'getAsCanvas');
    58306407                        },
    58316408
    58326409                        /**
    5833                         Retrieves image in it's current state as mOxie.Blob object. Cannot be run on empty or image in progress (throws
     6410                        Retrieves image in it's current state as moxie.file.Blob object. Cannot be run on empty or image in progress (throws
    58346411                        DOMException.INVALID_STATE_ERR).
    58356412
    58366413                        @method getAsBlob
     
    58766453                        },
    58776454
    58786455                        /**
    5879                         Embeds a visual representation of the image into the specified node. Depending on the runtime, 
    5880                         it might be a canvas, an img node or a thrid party shim object (Flash or SilverLight - very rare, 
     6456                        Embeds a visual representation of the image into the specified node. Depending on the runtime,
     6457                        it might be a canvas, an img node or a thrid party shim object (Flash or SilverLight - very rare,
    58816458                        can be used in legacy browsers that do not have canvas or proper dataURI support).
    58826459
    58836460                        @method embed
    58846461                        @param {DOMElement} el DOM element to insert the image object into
    5885                         @param {Object} [opts]
    5886                                 @param {Number} [opts.width] The width of an embed (defaults to the image width)
    5887                                 @param {Number} [opts.height] The height of an embed (defaults to the image height)
    5888                                 @param {String} [type="image/jpeg"] Mime type
    5889                                 @param {Number} [quality=90] Quality of an embed, if mime type is image/jpeg
    5890                                 @param {Boolean} [crop=false] Whether to crop an embed to the specified dimensions
     6462                        @param {Object} [options]
     6463                                @param {Number} [options.width] The width of an embed (defaults to the image width)
     6464                                @param {Number} [options.height] The height of an embed (defaults to the image height)
     6465                                @param {String} [options.type="image/jpeg"] Mime type
     6466                                @param {Number} [options.quality=90] Quality of an embed, if mime type is image/jpeg
     6467                                @param {Boolean} [options.crop=false] Whether to crop an embed to the specified dimensions
     6468                                @param {Boolean} [options.fit=true] By default thumbs will be up- or downscaled as necessary to fit the dimensions
    58916469                        */
    5892                         embed: function(el, opts) {
     6470                        embed: function(el, options) {
    58936471                                var self = this
    58946472                                , runtime // this has to be outside of all the closures to contain proper runtime
    58956473                                ;
    58966474
    5897                                 opts = Basic.extend({
     6475                                var opts = Basic.extend({
    58986476                                        width: this.width,
    58996477                                        height: this.height,
    59006478                                        type: this.type || 'image/jpeg',
    5901                                         quality: 90
    5902                                 }, opts || {});
    5903                                
     6479                                        quality: 90,
     6480                                        fit: true,
     6481                                        resample: 'nearest'
     6482                                }, options);
    59046483
     6484
    59056485                                function render(type, quality) {
    59066486                                        var img = this;
    59076487
     
    59236503                                        }
    59246504
    59256505                                        if (Env.can('use_data_uri_of', dataUrl.length)) {
    5926                                                 el.innerHTML = '<img src="' + dataUrl + '" width="' + img.width + '" height="' + img.height + '" />';
     6506                                                el.innerHTML = '<img src="' + dataUrl + '" width="' + img.width + '" height="' + img.height + '" alt="" />';
    59276507                                                img.destroy();
    59286508                                                self.trigger('embedded');
    59296509                                        } else {
     
    59766556                                        if (!this.size) { // only preloaded image objects can be used as source
    59776557                                                throw new x.DOMException(x.DOMException.INVALID_STATE_ERR);
    59786558                                        }
    5979                                        
     6559
    59806560                                        // high-resolution images cannot be consistently handled across the runtimes
    59816561                                        if (this.width > Image.MAX_RESIZE_WIDTH || this.height > Image.MAX_RESIZE_HEIGHT) {
    59826562                                                //throw new x.ImageError(x.ImageError.MAX_RESOLUTION_ERR);
     
    59896569                                        });
    59906570
    59916571                                        imgCopy.bind("Load", function() {
    5992                                                 imgCopy.downsize(opts);
     6572                                                this.downsize(opts);
    59936573                                        });
    59946574
    59956575                                        // if embedded thumb data is available and dimensions are big enough, use it
     
    60076587                        },
    60086588
    60096589                        /**
    6010                         Properly destroys the image and frees resources in use. If any. Recommended way to dispose mOxie.Image object.
     6590                        Properly destroys the image and frees resources in use. If any. Recommended way to dispose
     6591                        moxie.image.Image object.
    60116592
    60126593                        @method destroy
    60136594                        */
     
    60166597                                        this.getRuntime().exec.call(this, 'Image', 'destroy');
    60176598                                        this.disconnectRuntime();
    60186599                                }
     6600                                if (this.meta && this.meta.thumb) {
     6601                                        // thumb is blob, make sure we destroy it first
     6602                                        this.meta.thumb.data.destroy();
     6603                                }
    60196604                                this.unbindAll();
    60206605                        }
    60216606                });
     
    60256610                this.handleEventProps(dispatches);
    60266611
    60276612                this.bind('Load Resize', function() {
    6028                         _updateInfo.call(this);
     6613                        return _updateInfo.call(this); // if operation fails (e.g. image is neither PNG nor JPEG) cancel all pending events
    60296614                }, 999);
    60306615
    60316616
    60326617                function _updateInfo(info) {
    6033                         if (!info) {
    6034                                 info = this.exec('Image', 'getInfo');
    6035                         }
     6618                        try {
     6619                                if (!info) {
     6620                                        info = this.exec('Image', 'getInfo');
     6621                                }
    60366622
    6037                         this.size = info.size;
    6038                         this.width = info.width;
    6039                         this.height = info.height;
    6040                         this.type = info.type;
    6041                         this.meta = info.meta;
     6623                                this.size = info.size;
     6624                                this.width = info.width;
     6625                                this.height = info.height;
     6626                                this.type = info.type;
     6627                                this.meta = info.meta;
    60426628
    6043                         // update file name, only if empty
    6044                         if (this.name === '') {
    6045                                 this.name = info.name;
     6629                                // update file name, only if empty
     6630                                if (this.name === '') {
     6631                                        this.name = info.name;
     6632                                }
     6633
     6634                                return true;
     6635                        } catch(ex) {
     6636                                this.trigger('error', ex.code);
     6637                                return false;
    60466638                        }
    60476639                }
    6048                
    60496640
     6641
    60506642                function _load(src) {
    60516643                        var srcType = Basic.typeOf(src);
    60526644
     
    61676759
    61686760        // virtual world will crash on you if image has a resolution higher than this:
    61696761        Image.MAX_RESIZE_WIDTH = 8192;
    6170         Image.MAX_RESIZE_HEIGHT = 8192; 
     6762        Image.MAX_RESIZE_HEIGHT = 8192;
    61716763
    61726764        Image.prototype = EventTarget.instance;
    61736765
     
    62146806                                access_image_binary: function() {
    62156807                                        return I.can('access_binary') && !!extensions.Image;
    62166808                                },
    6217                                 display_media: Test(Env.can('create_canvas') || Env.can('use_data_uri_over32kb')),
     6809                                display_media: Test(
     6810                                        (Env.can('create_canvas') || Env.can('use_data_uri_over32kb')) &&
     6811                                        defined('moxie/image/Image')
     6812                                ),
    62186813                                do_cors: Test(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()),
    62196814                                drag_and_drop: Test(function() {
    62206815                                        // this comes directly from Modernizr: http://www.modernizr.com/
     
    62246819                                                (Env.browser !== 'IE' || Env.verComp(Env.version, 9, '>'));
    62256820                                }()),
    62266821                                filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
    6227                                         return (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '>=')) ||
    6228                                                 (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
    6229                                                 (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '>='));
     6822                                        return !(
     6823                                                (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '<')) ||
     6824                                                (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) ||
     6825                                                (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '<')) ||
     6826                                                (Env.browser === 'Firefox' && Env.verComp(Env.version, 37, '<'))
     6827                                        );
    62306828                                }()),
    62316829                                return_response_headers: True,
    62326830                                return_response_type: function(responseType) {
     
    62446842                                        return Env.can('use_fileinput') && window.File;
    62456843                                },
    62466844                                select_folder: function() {
    6247                                         return I.can('select_file') && Env.browser === 'Chrome' && Env.verComp(Env.version, 21, '>=');
     6845                                        return I.can('select_file') && (
     6846                                                Env.browser === 'Chrome' && Env.verComp(Env.version, 21, '>=') ||
     6847                                                Env.browser === 'Firefox' && Env.verComp(Env.version, 42, '>=') // https://developer.mozilla.org/en-US/Firefox/Releases/42
     6848                                        );
    62486849                                },
    62496850                                select_multiple: function() {
    62506851                                        // it is buggy on Safari Windows and iOS
     
    62626863                                        return I.can('slice_blob') && I.can('send_multipart');
    62636864                                },
    62646865                                summon_file_dialog: function() { // yeah... some dirty sniffing here...
    6265                                         return I.can('select_file') && (
    6266                                                 (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '>=')) ||
    6267                                                 (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '>=')) ||
    6268                                                 (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
    6269                                                 !!~Basic.inArray(Env.browser, ['Chrome', 'Safari'])
     6866                                        return I.can('select_file') && !(
     6867                                                (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '<')) ||
     6868                                                (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '<')) ||
     6869                                                (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<'))
    62706870                                        );
    62716871                                },
    6272                                 upload_filesize: True
     6872                                upload_filesize: True,
     6873                                use_http_method: True
    62736874                        },
    62746875                        arguments[2]
    62756876                );
     
    62996900        return extensions;
    63006901});
    63016902
     6903// Included from: src/javascript/runtime/html5/file/Blob.js
     6904
     6905/**
     6906 * Blob.js
     6907 *
     6908 * Copyright 2013, Moxiecode Systems AB
     6909 * Released under GPL License.
     6910 *
     6911 * License: http://www.plupload.com/license
     6912 * Contributing: http://www.plupload.com/contributing
     6913 */
     6914
     6915/**
     6916@class moxie/runtime/html5/file/Blob
     6917@private
     6918*/
     6919define("moxie/runtime/html5/file/Blob", [
     6920        "moxie/runtime/html5/Runtime",
     6921        "moxie/file/Blob"
     6922], function(extensions, Blob) {
     6923
     6924        function HTML5Blob() {
     6925                function w3cBlobSlice(blob, start, end) {
     6926                        var blobSlice;
     6927
     6928                        if (window.File.prototype.slice) {
     6929                                try {
     6930                                        blob.slice();   // depricated version will throw WRONG_ARGUMENTS_ERR exception
     6931                                        return blob.slice(start, end);
     6932                                } catch (e) {
     6933                                        // depricated slice method
     6934                                        return blob.slice(start, end - start);
     6935                                }
     6936                        // slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
     6937                        } else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) {
     6938                                return blobSlice.call(blob, start, end);
     6939                        } else {
     6940                                return null; // or throw some exception
     6941                        }
     6942                }
     6943
     6944                this.slice = function() {
     6945                        return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments));
     6946                };
     6947
     6948                this.destroy = function() {
     6949                        this.getRuntime().getShim().removeInstance(this.uid);
     6950                };
     6951        }
     6952
     6953        return (extensions.Blob = HTML5Blob);
     6954});
     6955
    63026956// Included from: src/javascript/core/utils/Events.js
    63036957
    63046958/**
     
    63116965 * Contributing: http://www.plupload.com/contributing
    63126966 */
    63136967
     6968/**
     6969@class moxie/core/utils/Events
     6970@public
     6971@static
     6972*/
     6973
    63146974define('moxie/core/utils/Events', [
    63156975        'moxie/core/utils/Basic'
    63166976], function(Basic) {
     
    63306990        in objects internal Plupload registry (@see removeEvent).
    63316991       
    63326992        @method addEvent
    6333         @for Utils
    63346993        @static
    63356994        @param {Object} obj DOM element like object to add handler to.
    63366995        @param {String} name Name to add event listener to.
     
    64997158], function(extensions, File, Basic, Dom, Events, Mime, Env) {
    65007159       
    65017160        function FileInput() {
    6502                 var _options;
     7161                var _options, _browseBtnZIndex; // save original z-index
    65037162
    65047163                Basic.extend(this, {
    65057164                        init: function(options) {
     
    65087167                                _options = options;
    65097168
    65107169                                // figure out accept string
    6511                                 mimes = _options.accept.mimes || Mime.extList2mimes(_options.accept, I.can('filter_by_extension'));
     7170                                mimes = Mime.extList2mimes(_options.accept, I.can('filter_by_extension'));
    65127171
    65137172                                shimContainer = I.getShimContainer();
    65147173
     
    65307189
    65317190
    65327191                                browseButton = Dom.get(_options.browse_button);
     7192                                _browseBtnZIndex = Dom.getStyle(browseButton, 'z-index') || 'auto';
    65337193
    65347194                                // Route click event to the input[type=file] element for browsers that support such behavior
    65357195                                if (I.can('summon_file_dialog')) {
     
    65377197                                                browseButton.style.position = 'relative';
    65387198                                        }
    65397199
    6540                                         zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
    6541 
    6542                                         browseButton.style.zIndex = zIndex;
    6543                                         shimContainer.style.zIndex = zIndex - 1;
    6544 
    65457200                                        Events.addEvent(browseButton, 'click', function(e) {
    65467201                                                var input = Dom.get(I.uid);
    65477202                                                if (input && !input.disabled) { // for some reason FF (up to 8.0.1 so far) lets to click disabled input[type=file]
     
    65497204                                                }
    65507205                                                e.preventDefault();
    65517206                                        }, comp.uid);
     7207
     7208                                        comp.bind('Refresh', function() {
     7209                                                zIndex = parseInt(_browseBtnZIndex, 10) || 1;
     7210
     7211                                                Dom.get(_options.browse_button).style.zIndex = zIndex;
     7212                                                this.getRuntime().getShimContainer().style.zIndex = zIndex - 1;
     7213                                        });
    65527214                                }
    65537215
    65547216                                /* Since we have to place input[type=file] on top of the browse_button for some browsers,
     
    65717233                                        comp.trigger('mouseup');
    65727234                                }, comp.uid);
    65737235
     7236                                // it shouldn't be possible to tab into the hidden element
     7237                                (I.can('summon_file_dialog') ? input : browseButton).setAttribute('tabindex', -1);
    65747238
    6575                                 input.onchange = function onChange(e) { // there should be only one handler for this
     7239                                input.onchange = function onChange() { // there should be only one handler for this
    65767240                                        comp.files = [];
    65777241
    65787242                                        Basic.each(this.files, function(file) {
     
    66217285                        },
    66227286
    66237287
     7288                        setOption: function(name, value) {
     7289                                var I = this.getRuntime();
     7290                                var input = Dom.get(I.uid);
     7291
     7292                                switch (name) {
     7293                                        case 'accept':
     7294                                                if (value) {
     7295                                                        var mimes = value.mimes || Mime.extList2mimes(value, I.can('filter_by_extension'));
     7296                                                        input.setAttribute('accept', mimes.join(','));
     7297                                                } else {
     7298                                                        input.removeAttribute('accept');
     7299                                                }
     7300                                                break;
     7301
     7302                                        case 'directory':
     7303                                                if (value && I.can('select_folder')) {
     7304                                                        input.setAttribute('directory', '');
     7305                                                        input.setAttribute('webkitdirectory', '');
     7306                                                } else {
     7307                                                        input.removeAttribute('directory');
     7308                                                        input.removeAttribute('webkitdirectory');
     7309                                                }
     7310                                                break;
     7311
     7312                                        case 'multiple':
     7313                                                if (value && I.can('select_multiple')) {
     7314                                                        input.setAttribute('multiple', '');
     7315                                                } else {
     7316                                                        input.removeAttribute('multiple');
     7317                                                }
     7318
     7319                                }
     7320                        },
     7321
     7322
    66247323                        disable: function(state) {
    66257324                                var I = this.getRuntime(), input;
    66267325
     
    66337332                                var I = this.getRuntime()
    66347333                                , shim = I.getShim()
    66357334                                , shimContainer = I.getShimContainer()
     7335                                , container = _options && Dom.get(_options.container)
     7336                                , browseButton = _options && Dom.get(_options.browse_button)
    66367337                                ;
    66377338                               
    6638                                 Events.removeAllEvents(shimContainer, this.uid);
    6639                                 Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
    6640                                 Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
     7339                                if (container) {
     7340                                        Events.removeAllEvents(container, this.uid);
     7341                                }
    66417342                               
     7343                                if (browseButton) {
     7344                                        Events.removeAllEvents(browseButton, this.uid);
     7345                                        browseButton.style.zIndex = _browseBtnZIndex; // reset to original value
     7346                                }
     7347                               
    66427348                                if (shimContainer) {
     7349                                        Events.removeAllEvents(shimContainer, this.uid);
    66437350                                        shimContainer.innerHTML = '';
    66447351                                }
    66457352
    66467353                                shim.removeInstance(this.uid);
    66477354
    6648                                 _options = shimContainer = shim = null;
     7355                                _options = shimContainer = container = browseButton = shim = null;
    66497356                        }
    66507357                });
    66517358        }
     
    66537360        return (extensions.FileInput = FileInput);
    66547361});
    66557362
    6656 // Included from: src/javascript/runtime/html5/file/Blob.js
    6657 
    6658 /**
    6659  * Blob.js
    6660  *
    6661  * Copyright 2013, Moxiecode Systems AB
    6662  * Released under GPL License.
    6663  *
    6664  * License: http://www.plupload.com/license
    6665  * Contributing: http://www.plupload.com/contributing
    6666  */
    6667 
    6668 /**
    6669 @class moxie/runtime/html5/file/Blob
    6670 @private
    6671 */
    6672 define("moxie/runtime/html5/file/Blob", [
    6673         "moxie/runtime/html5/Runtime",
    6674         "moxie/file/Blob"
    6675 ], function(extensions, Blob) {
    6676 
    6677         function HTML5Blob() {
    6678                 function w3cBlobSlice(blob, start, end) {
    6679                         var blobSlice;
    6680 
    6681                         if (window.File.prototype.slice) {
    6682                                 try {
    6683                                         blob.slice();   // depricated version will throw WRONG_ARGUMENTS_ERR exception
    6684                                         return blob.slice(start, end);
    6685                                 } catch (e) {
    6686                                         // depricated slice method
    6687                                         return blob.slice(start, end - start);
    6688                                 }
    6689                         // slice method got prefixed: https://bugzilla.mozilla.org/show_bug.cgi?id=649672
    6690                         } else if ((blobSlice = window.File.prototype.webkitSlice || window.File.prototype.mozSlice)) {
    6691                                 return blobSlice.call(blob, start, end);
    6692                         } else {
    6693                                 return null; // or throw some exception
    6694                         }
    6695                 }
    6696 
    6697                 this.slice = function() {
    6698                         return new Blob(this.getRuntime().uid, w3cBlobSlice.apply(this, arguments));
    6699                 };
    6700         }
    6701 
    6702         return (extensions.Blob = HTML5Blob);
    6703 });
    6704 
    67057363// Included from: src/javascript/runtime/html5/file/FileDrop.js
    67067364
    67077365/**
     
    67827440                        destroy: function() {
    67837441                                Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
    67847442                                _ruid = _files = _allowedExts = _options = null;
     7443                                this.getRuntime().getShim().removeInstance(this.uid);
    67857444                        }
    67867445                });
    67877446
     
    69747633
    69757634                        destroy: function() {
    69767635                                _fr = null;
     7636                                this.getRuntime().getShim().removeInstance(this.uid);
    69777637                        }
    69787638                });
    69797639
     
    71417801                                                                _xhr.onreadystatechange = function() {};
    71427802
    71437803                                                                // usually status 0 is returned when server is unreachable, but FF also fails to status 0 for 408 timeout
    7144                                                                 if (_xhr.status === 0) {
    7145                                                                         target.trigger('error');
    7146                                                                 } else {
    7147                                                                         target.trigger('load');
    7148                                                                 }                                                       
     7804                                                                try {
     7805                                                                        if (_xhr.status >= 200 && _xhr.status < 400) {
     7806                                                                                target.trigger('load');
     7807                                                                                break;
     7808                                                                        }
     7809                                                                } catch(ex) {}
     7810
     7811                                                                target.trigger('error');
    71497812                                                                break;
    71507813                                                }
    71517814                                        };
     
    72567919
    72577920                        destroy: function() {
    72587921                                self = _filename = null;
     7922                                this.getRuntime().getShim().removeInstance(this.uid);
    72597923                        }
    72607924                });
    72617925
     
    73888052                        UTF16StringReader.apply(this, arguments);
    73898053                }
    73908054        }
    7391          
    73928055
    73938056        Basic.extend(BinaryReader.prototype, {
    73948057               
     
    84889151 */
    84899152
    84909153/**
     9154Optional image investigation tool for HTML5 runtime. Provides the following features:
     9155- ability to distinguish image type (JPEG or PNG) by signature
     9156- ability to extract image width/height directly from it's internals, without preloading in memory (fast)
     9157- ability to extract APP headers from JPEGs (Exif, GPS, etc)
     9158- ability to replace width/height tags in extracted JPEG headers
     9159- ability to restore APP headers, that were for example stripped during image manipulation
     9160
    84919161@class moxie/runtime/html5/image/ImageInfo
    84929162@private
     9163@param {String} data Image source as binary string
    84939164*/
    84949165define("moxie/runtime/html5/image/ImageInfo", [
    84959166        "moxie/core/utils/Basic",
     
    84979168        "moxie/runtime/html5/image/JPEG",
    84989169        "moxie/runtime/html5/image/PNG"
    84999170], function(Basic, x, JPEG, PNG) {
    8500         /**
    8501         Optional image investigation tool for HTML5 runtime. Provides the following features:
    8502         - ability to distinguish image type (JPEG or PNG) by signature
    8503         - ability to extract image width/height directly from it's internals, without preloading in memory (fast)
    8504         - ability to extract APP headers from JPEGs (Exif, GPS, etc)
    8505         - ability to replace width/height tags in extracted JPEG headers
    8506         - ability to restore APP headers, that were for example stripped during image manipulation
    85079171
    8508         @class ImageInfo
    8509         @constructor
    8510         @param {String} data Image source as binary string
    8511         */
    85129172        return function(data) {
    85139173                var _cs = [JPEG, PNG], _img;
    85149174
     
    86119271        };
    86129272});
    86139273
    8614 // Included from: src/javascript/runtime/html5/image/MegaPixel.js
     9274// Included from: src/javascript/runtime/html5/image/ResizerCanvas.js
    86159275
    86169276/**
    8617 (The MIT License)
     9277 * ResizerCanvas.js
     9278 *
     9279 * Copyright 2013, Moxiecode Systems AB
     9280 * Released under GPL License.
     9281 *
     9282 * License: http://www.plupload.com/license
     9283 * Contributing: http://www.plupload.com/contributing
     9284 */
    86189285
    8619 Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>;
     9286/**
     9287 * Resizes image/canvas using canvas
     9288 */
     9289define("moxie/runtime/html5/image/ResizerCanvas", [], function() {
    86209290
    8621 Permission is hereby granted, free of charge, to any person obtaining
    8622 a copy of this software and associated documentation files (the
    8623 'Software'), to deal in the Software without restriction, including
    8624 without limitation the rights to use, copy, modify, merge, publish,
    8625 distribute, sublicense, and/or sell copies of the Software, and to
    8626 permit persons to whom the Software is furnished to do so, subject to
    8627 the following conditions:
     9291    function scale(image, ratio, resample) {
     9292        var sD = image.width > image.height ? 'width' : 'height'; // take the largest side
     9293        var dD = Math.round(image[sD] * ratio);
     9294        var scaleCapped = false;
    86289295
    8629 The above copyright notice and this permission notice shall be
    8630 included in all copies or substantial portions of the Software.
     9296        if (resample !== 'nearest' && (ratio < 0.5 || ratio > 2)) {
     9297            ratio = ratio < 0.5 ? 0.5 : 2;
     9298            scaleCapped = true;
     9299        }
    86319300
    8632 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
    8633 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    8634 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    8635 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    8636 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    8637 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    8638 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    8639 */
     9301        var tCanvas = _scale(image, ratio);
    86409302
    8641 /**
    8642  * Mega pixel image rendering library for iOS6 Safari
    8643  *
    8644  * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel),
    8645  * which causes unexpected subsampling when drawing it in canvas.
    8646  * By using this library, you can safely render the image with proper stretching.
    8647  *
    8648  * Copyright (c) 2012 Shinichi Tomita <shinichi.tomita@gmail.com>
    8649  * Released under the MIT license
    8650  */
     9303        if (scaleCapped) {
     9304            return scale(tCanvas, dD / tCanvas[sD], resample);
     9305        } else {
     9306            return tCanvas;
     9307        }
     9308    }
    86519309
    8652 /**
    8653 @class moxie/runtime/html5/image/MegaPixel
    8654 @private
    8655 */
    8656 define("moxie/runtime/html5/image/MegaPixel", [], function() {
    86579310
    8658         /**
    8659          * Rendering image element (with resizing) into the canvas element
    8660          */
    8661         function renderImageToCanvas(img, canvas, options) {
    8662                 var iw = img.naturalWidth, ih = img.naturalHeight;
    8663                 var width = options.width, height = options.height;
    8664                 var x = options.x || 0, y = options.y || 0;
    8665                 var ctx = canvas.getContext('2d');
    8666                 if (detectSubsampling(img)) {
    8667                         iw /= 2;
    8668                         ih /= 2;
    8669                 }
    8670                 var d = 1024; // size of tiling canvas
    8671                 var tmpCanvas = document.createElement('canvas');
    8672                 tmpCanvas.width = tmpCanvas.height = d;
    8673                 var tmpCtx = tmpCanvas.getContext('2d');
    8674                 var vertSquashRatio = detectVerticalSquash(img, iw, ih);
    8675                 var sy = 0;
    8676                 while (sy < ih) {
    8677                         var sh = sy + d > ih ? ih - sy : d;
    8678                         var sx = 0;
    8679                         while (sx < iw) {
    8680                                 var sw = sx + d > iw ? iw - sx : d;
    8681                                 tmpCtx.clearRect(0, 0, d, d);
    8682                                 tmpCtx.drawImage(img, -sx, -sy);
    8683                                 var dx = (sx * width / iw + x) << 0;
    8684                                 var dw = Math.ceil(sw * width / iw);
    8685                                 var dy = (sy * height / ih / vertSquashRatio + y) << 0;
    8686                                 var dh = Math.ceil(sh * height / ih / vertSquashRatio);
    8687                                 ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
    8688                                 sx += d;
    8689                         }
    8690                         sy += d;
    8691                 }
    8692                 tmpCanvas = tmpCtx = null;
    8693         }
     9311    function _scale(image, ratio) {
     9312        var sW = image.width;
     9313        var sH = image.height;
     9314        var dW = Math.round(sW * ratio);
     9315        var dH = Math.round(sH * ratio);
    86949316
    8695         /**
    8696          * Detect subsampling in loaded image.
    8697          * In iOS, larger images than 2M pixels may be subsampled in rendering.
    8698          */
    8699         function detectSubsampling(img) {
    8700                 var iw = img.naturalWidth, ih = img.naturalHeight;
    8701                 if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image
    8702                         var canvas = document.createElement('canvas');
    8703                         canvas.width = canvas.height = 1;
    8704                         var ctx = canvas.getContext('2d');
    8705                         ctx.drawImage(img, -iw + 1, 0);
    8706                         // subsampled image becomes half smaller in rendering size.
    8707                         // check alpha channel value to confirm image is covering edge pixel or not.
    8708                         // if alpha value is 0 image is not covering, hence subsampled.
    8709                         return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
    8710                 } else {
    8711                         return false;
    8712                 }
    8713         }
     9317        var canvas = document.createElement('canvas');
     9318        canvas.width = dW;
     9319        canvas.height = dH;
     9320        canvas.getContext("2d").drawImage(image, 0, 0, sW, sH, 0, 0, dW, dH);
    87149321
     9322        image = null; // just in case
     9323        return canvas;
     9324    }
    87159325
    8716         /**
    8717          * Detecting vertical squash in loaded image.
    8718          * Fixes a bug which squash image vertically while drawing into canvas for some images.
    8719          */
    8720         function detectVerticalSquash(img, iw, ih) {
    8721                 var canvas = document.createElement('canvas');
    8722                 canvas.width = 1;
    8723                 canvas.height = ih;
    8724                 var ctx = canvas.getContext('2d');
    8725                 ctx.drawImage(img, 0, 0);
    8726                 var data = ctx.getImageData(0, 0, 1, ih).data;
    8727                 // search image edge pixel position in case it is squashed vertically.
    8728                 var sy = 0;
    8729                 var ey = ih;
    8730                 var py = ih;
    8731                 while (py > sy) {
    8732                         var alpha = data[(py - 1) * 4 + 3];
    8733                         if (alpha === 0) {
    8734                                 ey = py;
    8735                         } else {
    8736                         sy = py;
    8737                         }
    8738                         py = (ey + sy) >> 1;
    8739                 }
    8740                 canvas = null;
    8741                 var ratio = (py / ih);
    8742                 return (ratio === 0) ? 1 : ratio;
    8743         }
     9326    return {
     9327        scale: scale
     9328    };
    87449329
    8745         return {
    8746                 isSubsampled: detectSubsampling,
    8747                 renderTo: renderImageToCanvas
    8748         };
    87499330});
    87509331
    87519332// Included from: src/javascript/runtime/html5/image/Image.js
     
    87729353        "moxie/file/Blob",
    87739354        "moxie/file/File",
    87749355        "moxie/runtime/html5/image/ImageInfo",
    8775         "moxie/runtime/html5/image/MegaPixel",
     9356        "moxie/runtime/html5/image/ResizerCanvas",
    87769357        "moxie/core/utils/Mime",
    87779358        "moxie/core/utils/Env"
    8778 ], function(extensions, Basic, x, Encode, Blob, File, ImageInfo, MegaPixel, Mime, Env) {
    8779        
     9359], function(extensions, Basic, x, Encode, Blob, File, ImageInfo, ResizerCanvas, Mime, Env) {
     9360
    87809361        function HTML5Image() {
    87819362                var me = this
    87829363                , _img, _imgInfo, _canvas, _binStr, _blob
     
    87869367
    87879368                Basic.extend(this, {
    87889369                        loadFromBlob: function(blob) {
    8789                                 var comp = this, I = comp.getRuntime()
     9370                                var I = this.getRuntime()
    87909371                                , asBinary = arguments.length > 1 ? arguments[1] : true
    87919372                                ;
    87929373
     
    88059386                                                if (asBinary) {
    88069387                                                        _binStr = _toBinary(dataUrl);
    88079388                                                }
    8808                                                 _preload.call(comp, dataUrl);
     9389                                                _preload.call(this, dataUrl);
    88099390                                        });
    88109391                                }
    88119392                        },
     
    88299410                                        _imgInfo = new ImageInfo(_binStr);
    88309411                                }
    88319412
     9413                                // this stuff below is definitely having fun with itself
    88329414                                info = {
    88339415                                        width: _getImg().width || 0,
    88349416                                        height: _getImg().height || 0,
     
    88359417                                        type: _blob.type || Mime.getFileMime(_blob.name),
    88369418                                        size: _binStr && _binStr.length || _blob.size || 0,
    88379419                                        name: _blob.name || '',
    8838                                         meta: _imgInfo && _imgInfo.meta || this.meta || {}
     9420                                        meta: null
    88399421                                };
    88409422
    8841                                 // store thumbnail data as blob
    8842                                 if (info.meta && info.meta.thumb && !(info.meta.thumb.data instanceof Blob)) {
    8843                                         info.meta.thumb.data = new Blob(null, {
    8844                                                 type: 'image/jpeg',
    8845                                                 data: info.meta.thumb.data
    8846                                         });
     9423                                if (_preserveHeaders) {
     9424                                        info.meta = _imgInfo && _imgInfo.meta || this.meta || {};
     9425
     9426                                        // if data was taken from ImageInfo it will be a binary string, so we convert it to blob
     9427                                        if (info.meta && info.meta.thumb && !(info.meta.thumb.data instanceof Blob)) {
     9428                                                info.meta.thumb.data = new Blob(null, {
     9429                                                        type: 'image/jpeg',
     9430                                                        data: info.meta.thumb.data
     9431                                                });
     9432                                        }
    88479433                                }
    88489434
    88499435                                return info;
    88509436                        },
    88519437
    8852                         downsize: function() {
    8853                                 _downsize.apply(this, arguments);
     9438
     9439                        resize: function(rect, ratio, options) {
     9440                                var canvas = document.createElement('canvas');
     9441                                canvas.width = rect.width;
     9442                                canvas.height = rect.height;
     9443
     9444                                canvas.getContext("2d").drawImage(_getImg(), rect.x, rect.y, rect.width, rect.height, 0, 0, canvas.width, canvas.height);
     9445
     9446                                _canvas = ResizerCanvas.scale(canvas, ratio);
     9447
     9448                                _preserveHeaders = options.preserveHeaders;
     9449
     9450                                // rotate if required, according to orientation tag
     9451                                if (!_preserveHeaders) {
     9452                                        var orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
     9453                                        _canvas = _rotateToOrientaion(_canvas, orientation);
     9454                                }
     9455
     9456                                this.width = _canvas.width;
     9457                                this.height = _canvas.height;
     9458
     9459                                _modified = true;
     9460
     9461                                this.trigger('Resize');
    88549462                        },
    88559463
    88569464                        getAsCanvas: function() {
    8857                                 if (_canvas) {
    8858                                         _canvas.id = this.uid + '_canvas';
     9465                                if (!_canvas) {
     9466                                        _canvas = _getCanvas();
    88599467                                }
     9468                                _canvas.id = this.uid + '_canvas';
    88609469                                return _canvas;
    88619470                        },
    88629471
    88639472                        getAsBlob: function(type, quality) {
    88649473                                if (type !== this.type) {
    8865                                         // if different mime type requested prepare image for conversion
    8866                                         _downsize.call(this, this.width, this.height, false);
     9474                                        _modified = true; // reconsider the state
     9475                                        return new File(null, {
     9476                                                name: _blob.name || '',
     9477                                                type: type,
     9478                                                data: me.getAsDataURL(type, quality)
     9479                                        });
    88679480                                }
    88689481                                return new File(null, {
    88699482                                        name: _blob.name || '',
    88709483                                        type: type,
    8871                                         data: me.getAsBinaryString.call(this, type, quality)
     9484                                        data: me.getAsBinaryString(type, quality)
    88729485                                });
    88739486                        },
    88749487
     
    88809493                                        return _img.src;
    88819494                                }
    88829495
     9496                                // make sure we have a canvas to work with
     9497                                _getCanvas();
     9498
    88839499                                if ('image/jpeg' !== type) {
    88849500                                        return _canvas.toDataURL('image/png');
    88859501                                } else {
     
    89129528                                                quality = 90;
    89139529                                        }
    89149530
     9531                                        // make sure we have a canvas to work with
     9532                                        _getCanvas();
     9533
    89159534                                        try {
    89169535                                                // older Geckos used to result in an exception on quality argument
    89179536                                                dataUrl = _canvas.toDataURL('image/jpeg', quality/100);
     
    89649583                }
    89659584
    89669585
     9586                function _getCanvas() {
     9587                        var canvas = _getImg();
     9588                        if (canvas.nodeName.toLowerCase() == 'canvas') {
     9589                                return canvas;
     9590                        }
     9591                        _canvas = document.createElement('canvas');
     9592                        _canvas.width = canvas.width;
     9593                        _canvas.height = canvas.height;
     9594                        _canvas.getContext("2d").drawImage(canvas, 0, 0);
     9595                        return _canvas;
     9596                }
     9597
     9598
    89679599                function _toBinary(str) {
    89689600                        return Encode.atob(str.substring(str.indexOf('base64,') + 7));
    89699601                }
     
    89979629                        if (window.FileReader) {
    89989630                                fr = new FileReader();
    89999631                                fr.onload = function() {
    9000                                         callback(this.result);
     9632                                        callback.call(comp, this.result);
    90019633                                };
    90029634                                fr.onerror = function() {
    90039635                                        comp.trigger('error', x.ImageError.WRONG_FORMAT);
     
    90049636                                };
    90059637                                fr.readAsDataURL(file);
    90069638                        } else {
    9007                                 return callback(file.getAsDataURL());
     9639                                return callback.call(this, file.getAsDataURL());
    90089640                        }
    90099641                }
    90109642
    9011                 function _downsize(width, height, crop, preserveHeaders) {
    9012                         var self = this
    9013                         , scale
    9014                         , mathFn
    9015                         , x = 0
    9016                         , y = 0
    9017                         , img
    9018                         , destWidth
    9019                         , destHeight
    9020                         , orientation
    9021                         ;
    9022 
    9023                         _preserveHeaders = preserveHeaders; // we will need to check this on export (see getAsBinaryString())
    9024 
    9025                         // take into account orientation tag
    9026                         orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
    9027 
    9028                         if (Basic.inArray(orientation, [5,6,7,8]) !== -1) { // values that require 90 degree rotation
    9029                                 // swap dimensions
    9030                                 var tmp = width;
    9031                                 width = height;
    9032                                 height = tmp;
    9033                         }
    9034 
    9035                         img = _getImg();
    9036 
    9037                         // unify dimensions
    9038                         if (!crop) {
    9039                                 scale = Math.min(width/img.width, height/img.height);
    9040                         } else {
    9041                                 // one of the dimensions may exceed the actual image dimensions - we need to take the smallest value
    9042                                 width = Math.min(width, img.width);
    9043                                 height = Math.min(height, img.height);
    9044 
    9045                                 scale = Math.max(width/img.width, height/img.height);
    9046                         }
    9047                
    9048                         // we only downsize here
    9049                         if (scale > 1 && !crop && preserveHeaders) {
    9050                                 this.trigger('Resize');
    9051                                 return;
    9052                         }
    9053 
    9054                         // prepare canvas if necessary
    9055                         if (!_canvas) {
    9056                                 _canvas = document.createElement("canvas");
    9057                         }
    9058 
    9059                         // calculate dimensions of proportionally resized image
    9060                         destWidth = Math.round(img.width * scale);     
    9061                         destHeight = Math.round(img.height * scale);
    9062 
    9063                         // scale image and canvas
    9064                         if (crop) {
    9065                                 _canvas.width = width;
    9066                                 _canvas.height = height;
    9067 
    9068                                 // if dimensions of the resulting image still larger than canvas, center it
    9069                                 if (destWidth > width) {
    9070                                         x = Math.round((destWidth - width) / 2);
    9071                                 }
    9072 
    9073                                 if (destHeight > height) {
    9074                                         y = Math.round((destHeight - height) / 2);
    9075                                 }
    9076                         } else {
    9077                                 _canvas.width = destWidth;
    9078                                 _canvas.height = destHeight;
    9079                         }
    9080 
    9081                         // rotate if required, according to orientation tag
    9082                         if (!_preserveHeaders) {
    9083                                 _rotateToOrientaion(_canvas.width, _canvas.height, orientation);
    9084                         }
    9085 
    9086                         _drawToCanvas.call(this, img, _canvas, -x, -y, destWidth, destHeight);
    9087 
    9088                         this.width = _canvas.width;
    9089                         this.height = _canvas.height;
    9090 
    9091                         _modified = true;
    9092                         self.trigger('Resize');
    9093                 }
    9094 
    9095 
    9096                 function _drawToCanvas(img, canvas, x, y, w, h) {
    9097                         if (Env.OS === 'iOS') {
    9098                                 // avoid squish bug in iOS6
    9099                                 MegaPixel.renderTo(img, canvas, { width: w, height: h, x: x, y: y });
    9100                         } else {
    9101                                 var ctx = canvas.getContext('2d');
    9102                                 ctx.drawImage(img, x, y, w, h);
    9103                         }
    9104                 }
    9105 
    9106 
    91079643                /**
    91089644                * Transform canvas coordination according to specified frame size and orientation
    91099645                * Orientation value is from EXIF tag
    91109646                * @author Shinichi Tomita <shinichi.tomita@gmail.com>
    91119647                */
    9112                 function _rotateToOrientaion(width, height, orientation) {
    9113                         switch (orientation) {
    9114                                 case 5:
    9115                                 case 6:
    9116                                 case 7:
    9117                                 case 8:
    9118                                         _canvas.width = height;
    9119                                         _canvas.height = width;
    9120                                         break;
    9121                                 default:
    9122                                         _canvas.width = width;
    9123                                         _canvas.height = height;
     9648                function _rotateToOrientaion(img, orientation) {
     9649                        var RADIANS = Math.PI/180;
     9650                        var canvas = document.createElement('canvas');
     9651                        var ctx = canvas.getContext('2d');
     9652                        var width = img.width;
     9653                        var height = img.height;
     9654
     9655                        if (Basic.inArray(orientation, [5,6,7,8]) > -1) {
     9656                                canvas.width = height;
     9657                                canvas.height = width;
     9658                        } else {
     9659                                canvas.width = width;
     9660                                canvas.height = height;
    91249661                        }
    91259662
    91269663                        /**
     
    91339670                        7 = The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
    91349671                        8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
    91359672                        */
    9136 
    9137                         var ctx = _canvas.getContext('2d');
    91389673                        switch (orientation) {
    91399674                                case 2:
    91409675                                        // horizontal flip
     
    91449679                                case 3:
    91459680                                        // 180 rotate left
    91469681                                        ctx.translate(width, height);
    9147                                         ctx.rotate(Math.PI);
     9682                                        ctx.rotate(180 * RADIANS);
    91489683                                        break;
    91499684                                case 4:
    91509685                                        // vertical flip
     
    91539688                                        break;
    91549689                                case 5:
    91559690                                        // vertical flip + 90 rotate right
    9156                                         ctx.rotate(0.5 * Math.PI);
     9691                                        ctx.rotate(90 * RADIANS);
    91579692                                        ctx.scale(1, -1);
    91589693                                        break;
    91599694                                case 6:
    91609695                                        // 90 rotate right
    9161                                         ctx.rotate(0.5 * Math.PI);
     9696                                        ctx.rotate(90 * RADIANS);
    91629697                                        ctx.translate(0, -height);
    91639698                                        break;
    91649699                                case 7:
    91659700                                        // horizontal flip + 90 rotate right
    9166                                         ctx.rotate(0.5 * Math.PI);
     9701                                        ctx.rotate(90 * RADIANS);
    91679702                                        ctx.translate(width, -height);
    91689703                                        ctx.scale(-1, 1);
    91699704                                        break;
    91709705                                case 8:
    91719706                                        // 90 rotate left
    9172                                         ctx.rotate(-0.5 * Math.PI);
     9707                                        ctx.rotate(-90 * RADIANS);
    91739708                                        ctx.translate(-width, 0);
    91749709                                        break;
    91759710                        }
     9711
     9712                        ctx.drawImage(img, 0, 0, width, height);
     9713                        return canvas;
    91769714                }
    91779715
    91789716
     
    91819719                                _imgInfo.purge();
    91829720                                _imgInfo = null;
    91839721                        }
     9722
    91849723                        _binStr = _img = _canvas = _blob = null;
    91859724                        _modified = false;
    91869725                }
     
    91899728        return (extensions.Image = HTML5Image);
    91909729});
    91919730
     9731// Included from: src/javascript/runtime/flash/Runtime.js
     9732
    91929733/**
    9193  * Stub for moxie/runtime/flash/Runtime
    9194  * @private
     9734 * Runtime.js
     9735 *
     9736 * Copyright 2013, Moxiecode Systems AB
     9737 * Released under GPL License.
     9738 *
     9739 * License: http://www.plupload.com/license
     9740 * Contributing: http://www.plupload.com/contributing
    91959741 */
     9742
     9743/*global ActiveXObject:true */
     9744
     9745/**
     9746Defines constructor for Flash runtime.
     9747
     9748@class moxie/runtime/flash/Runtime
     9749@private
     9750*/
    91969751define("moxie/runtime/flash/Runtime", [
    9197 ], function() {
    9198         return {};
     9752        "moxie/core/utils/Basic",
     9753        "moxie/core/utils/Env",
     9754        "moxie/core/utils/Dom",
     9755        "moxie/core/Exceptions",
     9756        "moxie/runtime/Runtime"
     9757], function(Basic, Env, Dom, x, Runtime) {
     9758       
     9759        var type = 'flash', extensions = {};
     9760
     9761        /**
     9762        Get the version of the Flash Player
     9763
     9764        @method getShimVersion
     9765        @private
     9766        @return {Number} Flash Player version
     9767        */
     9768        function getShimVersion() {
     9769                var version;
     9770
     9771                try {
     9772                        version = navigator.plugins['Shockwave Flash'];
     9773                        version = version.description;
     9774                } catch (e1) {
     9775                        try {
     9776                                version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
     9777                        } catch (e2) {
     9778                                version = '0.0';
     9779                        }
     9780                }
     9781                version = version.match(/\d+/g);
     9782                return parseFloat(version[0] + '.' + version[1]);
     9783        }
     9784
     9785
     9786        /**
     9787        Cross-browser SWF removal
     9788        - Especially needed to safely and completely remove a SWF in Internet Explorer
     9789
     9790        Originated from SWFObject v2.2 <http://code.google.com/p/swfobject/>
     9791        */
     9792        function removeSWF(id) {
     9793        var obj = Dom.get(id);
     9794        if (obj && obj.nodeName == "OBJECT") {
     9795            if (Env.browser === 'IE') {
     9796                obj.style.display = "none";
     9797                (function onInit(){
     9798                        // http://msdn.microsoft.com/en-us/library/ie/ms534360(v=vs.85).aspx
     9799                    if (obj.readyState == 4) {
     9800                        removeObjectInIE(id);
     9801                    }
     9802                    else {
     9803                        setTimeout(onInit, 10);
     9804                    }
     9805                })();
     9806            }
     9807            else {
     9808                obj.parentNode.removeChild(obj);
     9809            }
     9810        }
     9811    }
     9812
     9813
     9814        function removeObjectInIE(id) {
     9815        var obj = Dom.get(id);
     9816        if (obj) {
     9817            for (var i in obj) {
     9818                if (typeof obj[i] == "function") {
     9819                    obj[i] = null;
     9820                }
     9821            }
     9822            obj.parentNode.removeChild(obj);
     9823        }
     9824    }
     9825
     9826        /**
     9827        Constructor for the Flash Runtime
     9828        */
     9829        function FlashRuntime(options) {
     9830                var I = this, initTimer;
     9831
     9832                options = Basic.extend({ swf_url: Env.swf_url }, options);
     9833
     9834                Runtime.call(this, options, type, {
     9835                        access_binary: function(value) {
     9836                                return value && I.mode === 'browser';
     9837                        },
     9838                        access_image_binary: function(value) {
     9839                                return value && I.mode === 'browser';
     9840                        },
     9841                        display_media: Runtime.capTest(defined('moxie/image/Image')),
     9842                        do_cors: Runtime.capTrue,
     9843                        drag_and_drop: false,
     9844                        report_upload_progress: function() {
     9845                                return I.mode === 'client';
     9846                        },
     9847                        resize_image: Runtime.capTrue,
     9848                        return_response_headers: false,
     9849                        return_response_type: function(responseType) {
     9850                                if (responseType === 'json' && !!window.JSON) {
     9851                                        return true;
     9852                                }
     9853                                return !Basic.arrayDiff(responseType, ['', 'text', 'document']) || I.mode === 'browser';
     9854                        },
     9855                        return_status_code: function(code) {
     9856                                return I.mode === 'browser' || !Basic.arrayDiff(code, [200, 404]);
     9857                        },
     9858                        select_file: Runtime.capTrue,
     9859                        select_multiple: Runtime.capTrue,
     9860                        send_binary_string: function(value) {
     9861                                return value && I.mode === 'browser';
     9862                        },
     9863                        send_browser_cookies: function(value) {
     9864                                return value && I.mode === 'browser';
     9865                        },
     9866                        send_custom_headers: function(value) {
     9867                                return value && I.mode === 'browser';
     9868                        },
     9869                        send_multipart: Runtime.capTrue,
     9870                        slice_blob: function(value) {
     9871                                return value && I.mode === 'browser';
     9872                        },
     9873                        stream_upload: function(value) {
     9874                                return value && I.mode === 'browser';
     9875                        },
     9876                        summon_file_dialog: false,
     9877                        upload_filesize: function(size) {
     9878                                return Basic.parseSizeStr(size) <= 2097152 || I.mode === 'client';
     9879                        },
     9880                        use_http_method: function(methods) {
     9881                                return !Basic.arrayDiff(methods, ['GET', 'POST']);
     9882                        }
     9883                }, {
     9884                        // capabilities that require specific mode
     9885                        access_binary: function(value) {
     9886                                return value ? 'browser' : 'client';
     9887                        },
     9888                        access_image_binary: function(value) {
     9889                                return value ? 'browser' : 'client';
     9890                        },
     9891                        report_upload_progress: function(value) {
     9892                                return value ? 'browser' : 'client';
     9893                        },
     9894                        return_response_type: function(responseType) {
     9895                                return Basic.arrayDiff(responseType, ['', 'text', 'json', 'document']) ? 'browser' : ['client', 'browser'];
     9896                        },
     9897                        return_status_code: function(code) {
     9898                                return Basic.arrayDiff(code, [200, 404]) ? 'browser' : ['client', 'browser'];
     9899                        },
     9900                        send_binary_string: function(value) {
     9901                                return value ? 'browser' : 'client';
     9902                        },
     9903                        send_browser_cookies: function(value) {
     9904                                return value ? 'browser' : 'client';
     9905                        },
     9906                        send_custom_headers: function(value) {
     9907                                return value ? 'browser' : 'client';
     9908                        },
     9909                        slice_blob: function(value) {
     9910                                return value ? 'browser' : 'client';
     9911                        },
     9912                        stream_upload: function(value) {
     9913                                return value ? 'client' : 'browser';
     9914                        },
     9915                        upload_filesize: function(size) {
     9916                                return Basic.parseSizeStr(size) >= 2097152 ? 'client' : 'browser';
     9917                        }
     9918                }, 'client');
     9919
     9920
     9921                // minimal requirement for Flash Player version
     9922                if (getShimVersion() < 11.3) {
     9923                        if (MXI_DEBUG && Env.debug.runtime) {
     9924                                Env.log("\tFlash didn't meet minimal version requirement (11.3).");     
     9925                        }
     9926
     9927                        this.mode = false; // with falsy mode, runtime won't operable, no matter what the mode was before
     9928                }
     9929
     9930
     9931                Basic.extend(this, {
     9932
     9933                        getShim: function() {
     9934                                return Dom.get(this.uid);
     9935                        },
     9936
     9937                        shimExec: function(component, action) {
     9938                                var args = [].slice.call(arguments, 2);
     9939                                return I.getShim().exec(this.uid, component, action, args);
     9940                        },
     9941
     9942                        init: function() {
     9943                                var html, el, container;
     9944
     9945                                container = this.getShimContainer();
     9946
     9947                                // if not the minimal height, shims are not initialized in older browsers (e.g FF3.6, IE6,7,8, Safari 4.0,5.0, etc)
     9948                                Basic.extend(container.style, {
     9949                                        position: 'absolute',
     9950                                        top: '-8px',
     9951                                        left: '-8px',
     9952                                        width: '9px',
     9953                                        height: '9px',
     9954                                        overflow: 'hidden'
     9955                                });
     9956
     9957                                // insert flash object
     9958                                html = '<object id="' + this.uid + '" type="application/x-shockwave-flash" data="' +  options.swf_url + '" ';
     9959
     9960                                if (Env.browser === 'IE') {
     9961                                        html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
     9962                                }
     9963
     9964                                html += 'width="100%" height="100%" style="outline:0">'  +
     9965                                        '<param name="movie" value="' + options.swf_url + '" />' +
     9966                                        '<param name="flashvars" value="uid=' + escape(this.uid) + '&target=' + Runtime.getGlobalEventTarget() + '" />' +
     9967                                        '<param name="wmode" value="transparent" />' +
     9968                                        '<param name="allowscriptaccess" value="always" />' +
     9969                                '</object>';
     9970
     9971                                if (Env.browser === 'IE') {
     9972                                        el = document.createElement('div');
     9973                                        container.appendChild(el);
     9974                                        el.outerHTML = html;
     9975                                        el = container = null; // just in case
     9976                                } else {
     9977                                        container.innerHTML = html;
     9978                                }
     9979
     9980                                // Init is dispatched by the shim
     9981                                initTimer = setTimeout(function() {
     9982                                        if (I && !I.initialized) { // runtime might be already destroyed by this moment
     9983                                                I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
     9984
     9985                                                if (MXI_DEBUG && Env.debug.runtime) {
     9986                                                        Env.log("\tFlash failed to initialize within a specified period of time (typically 5s).");     
     9987                                                }
     9988                                        }
     9989                                }, 5000);
     9990                        },
     9991
     9992                        destroy: (function(destroy) { // extend default destroy method
     9993                                return function() {
     9994                                        removeSWF(I.uid); // SWF removal requires special care in IE
     9995
     9996                                        destroy.call(I);
     9997                                        clearTimeout(initTimer); // initialization check might be still onwait
     9998                                        options = initTimer = destroy = I = null;
     9999                                };
     10000                        }(this.destroy))
     10001
     10002                }, extensions);
     10003        }
     10004
     10005        Runtime.addConstructor(type, FlashRuntime);
     10006
     10007        return extensions;
    919910008});
    920010009
     10010// Included from: src/javascript/runtime/flash/file/Blob.js
     10011
    920110012/**
    9202  * Stub for moxie/runtime/silverlight/Runtime
    9203  * @private
     10013 * Blob.js
     10014 *
     10015 * Copyright 2013, Moxiecode Systems AB
     10016 * Released under GPL License.
     10017 *
     10018 * License: http://www.plupload.com/license
     10019 * Contributing: http://www.plupload.com/contributing
    920410020 */
     10021
     10022/**
     10023@class moxie/runtime/flash/file/Blob
     10024@private
     10025*/
     10026define("moxie/runtime/flash/file/Blob", [
     10027        "moxie/runtime/flash/Runtime",
     10028        "moxie/file/Blob"
     10029], function(extensions, Blob) {
     10030
     10031        var FlashBlob = {
     10032                slice: function(blob, start, end, type) {
     10033                        var self = this.getRuntime();
     10034
     10035                        if (start < 0) {
     10036                                start = Math.max(blob.size + start, 0);
     10037                        } else if (start > 0) {
     10038                                start = Math.min(start, blob.size);
     10039                        }
     10040
     10041                        if (end < 0) {
     10042                                end = Math.max(blob.size + end, 0);
     10043                        } else if (end > 0) {
     10044                                end = Math.min(end, blob.size);
     10045                        }
     10046
     10047                        blob = self.shimExec.call(this, 'Blob', 'slice', start, end, type || '');
     10048
     10049                        if (blob) {
     10050                                blob = new Blob(self.uid, blob);
     10051                        }
     10052                        return blob;
     10053                }
     10054        };
     10055
     10056        return (extensions.Blob = FlashBlob);
     10057});
     10058
     10059// Included from: src/javascript/runtime/flash/file/FileInput.js
     10060
     10061/**
     10062 * FileInput.js
     10063 *
     10064 * Copyright 2013, Moxiecode Systems AB
     10065 * Released under GPL License.
     10066 *
     10067 * License: http://www.plupload.com/license
     10068 * Contributing: http://www.plupload.com/contributing
     10069 */
     10070
     10071/**
     10072@class moxie/runtime/flash/file/FileInput
     10073@private
     10074*/
     10075define("moxie/runtime/flash/file/FileInput", [
     10076        "moxie/runtime/flash/Runtime",
     10077        "moxie/file/File",
     10078        "moxie/core/utils/Dom",
     10079        "moxie/core/utils/Basic"
     10080], function(extensions, File, Dom, Basic) {
     10081
     10082        var FileInput = {
     10083                init: function(options) {
     10084                        var comp = this, I = this.getRuntime();
     10085                        var browseButton = Dom.get(options.browse_button);
     10086
     10087                        if (browseButton) {
     10088                                browseButton.setAttribute('tabindex', -1);
     10089                                browseButton = null;
     10090                        }
     10091
     10092                        this.bind("Change", function() {
     10093                                var files = I.shimExec.call(comp, 'FileInput', 'getFiles');
     10094                                comp.files = [];
     10095                                Basic.each(files, function(file) {
     10096                                        comp.files.push(new File(I.uid, file));
     10097                                });
     10098                        }, 999);
     10099
     10100                        this.getRuntime().shimExec.call(this, 'FileInput', 'init', {
     10101                                accept: options.accept,
     10102                                multiple: options.multiple
     10103                        });
     10104
     10105                        this.trigger('ready');
     10106                }
     10107        };
     10108
     10109        return (extensions.FileInput = FileInput);
     10110});
     10111
     10112// Included from: src/javascript/runtime/flash/file/FileReader.js
     10113
     10114/**
     10115 * FileReader.js
     10116 *
     10117 * Copyright 2013, Moxiecode Systems AB
     10118 * Released under GPL License.
     10119 *
     10120 * License: http://www.plupload.com/license
     10121 * Contributing: http://www.plupload.com/contributing
     10122 */
     10123
     10124/**
     10125@class moxie/runtime/flash/file/FileReader
     10126@private
     10127*/
     10128define("moxie/runtime/flash/file/FileReader", [
     10129        "moxie/runtime/flash/Runtime",
     10130        "moxie/core/utils/Encode"
     10131], function(extensions, Encode) {
     10132
     10133        function _formatData(data, op) {
     10134                switch (op) {
     10135                        case 'readAsText':
     10136                                return Encode.atob(data, 'utf8');
     10137                        case 'readAsBinaryString':
     10138                                return Encode.atob(data);
     10139                        case 'readAsDataURL':
     10140                                return data;
     10141                }
     10142                return null;
     10143        }
     10144
     10145        var FileReader = {
     10146                read: function(op, blob) {
     10147                        var comp = this;
     10148
     10149                        comp.result = '';
     10150
     10151                        // special prefix for DataURL read mode
     10152                        if (op === 'readAsDataURL') {
     10153                                comp.result = 'data:' + (blob.type || '') + ';base64,';
     10154                        }
     10155
     10156                        comp.bind('Progress', function(e, data) {
     10157                                if (data) {
     10158                                        comp.result += _formatData(data, op);
     10159                                }
     10160                        }, 999);
     10161
     10162                        return comp.getRuntime().shimExec.call(this, 'FileReader', 'readAsBase64', blob.uid);
     10163                }
     10164        };
     10165
     10166        return (extensions.FileReader = FileReader);
     10167});
     10168
     10169// Included from: src/javascript/runtime/flash/file/FileReaderSync.js
     10170
     10171/**
     10172 * FileReaderSync.js
     10173 *
     10174 * Copyright 2013, Moxiecode Systems AB
     10175 * Released under GPL License.
     10176 *
     10177 * License: http://www.plupload.com/license
     10178 * Contributing: http://www.plupload.com/contributing
     10179 */
     10180
     10181/**
     10182@class moxie/runtime/flash/file/FileReaderSync
     10183@private
     10184*/
     10185define("moxie/runtime/flash/file/FileReaderSync", [
     10186        "moxie/runtime/flash/Runtime",
     10187        "moxie/core/utils/Encode"
     10188], function(extensions, Encode) {
     10189       
     10190        function _formatData(data, op) {
     10191                switch (op) {
     10192                        case 'readAsText':
     10193                                return Encode.atob(data, 'utf8');
     10194                        case 'readAsBinaryString':
     10195                                return Encode.atob(data);
     10196                        case 'readAsDataURL':
     10197                                return data;
     10198                }
     10199                return null;
     10200        }
     10201
     10202        var FileReaderSync = {
     10203                read: function(op, blob) {
     10204                        var result, self = this.getRuntime();
     10205
     10206                        result = self.shimExec.call(this, 'FileReaderSync', 'readAsBase64', blob.uid);
     10207                        if (!result) {
     10208                                return null; // or throw ex
     10209                        }
     10210
     10211                        // special prefix for DataURL read mode
     10212                        if (op === 'readAsDataURL') {
     10213                                result = 'data:' + (blob.type || '') + ';base64,' + result;
     10214                        }
     10215
     10216                        return _formatData(result, op, blob.type);
     10217                }
     10218        };
     10219
     10220        return (extensions.FileReaderSync = FileReaderSync);
     10221});
     10222
     10223// Included from: src/javascript/runtime/flash/runtime/Transporter.js
     10224
     10225/**
     10226 * Transporter.js
     10227 *
     10228 * Copyright 2013, Moxiecode Systems AB
     10229 * Released under GPL License.
     10230 *
     10231 * License: http://www.plupload.com/license
     10232 * Contributing: http://www.plupload.com/contributing
     10233 */
     10234
     10235/**
     10236@class moxie/runtime/flash/runtime/Transporter
     10237@private
     10238*/
     10239define("moxie/runtime/flash/runtime/Transporter", [
     10240        "moxie/runtime/flash/Runtime",
     10241        "moxie/file/Blob"
     10242], function(extensions, Blob) {
     10243
     10244        var Transporter = {
     10245                getAsBlob: function(type) {
     10246                        var self = this.getRuntime()
     10247                        , blob = self.shimExec.call(this, 'Transporter', 'getAsBlob', type)
     10248                        ;
     10249                        if (blob) {
     10250                                return new Blob(self.uid, blob);
     10251                        }
     10252                        return null;
     10253                }
     10254        };
     10255
     10256        return (extensions.Transporter = Transporter);
     10257});
     10258
     10259// Included from: src/javascript/runtime/flash/xhr/XMLHttpRequest.js
     10260
     10261/**
     10262 * XMLHttpRequest.js
     10263 *
     10264 * Copyright 2013, Moxiecode Systems AB
     10265 * Released under GPL License.
     10266 *
     10267 * License: http://www.plupload.com/license
     10268 * Contributing: http://www.plupload.com/contributing
     10269 */
     10270
     10271/**
     10272@class moxie/runtime/flash/xhr/XMLHttpRequest
     10273@private
     10274*/
     10275define("moxie/runtime/flash/xhr/XMLHttpRequest", [
     10276        "moxie/runtime/flash/Runtime",
     10277        "moxie/core/utils/Basic",
     10278        "moxie/file/Blob",
     10279        "moxie/file/File",
     10280        "moxie/file/FileReaderSync",
     10281        "moxie/runtime/flash/file/FileReaderSync",
     10282        "moxie/xhr/FormData",
     10283        "moxie/runtime/Transporter",
     10284        "moxie/runtime/flash/runtime/Transporter"
     10285], function(extensions, Basic, Blob, File, FileReaderSync, FileReaderSyncFlash, FormData, Transporter, TransporterFlash) {
     10286       
     10287        var XMLHttpRequest = {
     10288
     10289                send: function(meta, data) {
     10290                        var target = this, self = target.getRuntime();
     10291
     10292                        function send() {
     10293                                meta.transport = self.mode;
     10294                                self.shimExec.call(target, 'XMLHttpRequest', 'send', meta, data);
     10295                        }
     10296
     10297
     10298                        function appendBlob(name, blob) {
     10299                                self.shimExec.call(target, 'XMLHttpRequest', 'appendBlob', name, blob.uid);
     10300                                data = null;
     10301                                send();
     10302                        }
     10303
     10304
     10305                        function attachBlob(blob, cb) {
     10306                                var tr = new Transporter();
     10307
     10308                                tr.bind("TransportingComplete", function() {
     10309                                        cb(this.result);
     10310                                });
     10311
     10312                                tr.transport(blob.getSource(), blob.type, {
     10313                                        ruid: self.uid
     10314                                });
     10315                        }
     10316
     10317                        // copy over the headers if any
     10318                        if (!Basic.isEmptyObj(meta.headers)) {
     10319                                Basic.each(meta.headers, function(value, header) {
     10320                                        self.shimExec.call(target, 'XMLHttpRequest', 'setRequestHeader', header, value.toString()); // Silverlight doesn't accept integers into the arguments of type object
     10321                                });
     10322                        }
     10323
     10324                        // transfer over multipart params and blob itself
     10325                        if (data instanceof FormData) {
     10326                                var blobField;
     10327                                data.each(function(value, name) {
     10328                                        if (value instanceof Blob) {
     10329                                                blobField = name;
     10330                                        } else {
     10331                                                self.shimExec.call(target, 'XMLHttpRequest', 'append', name, value);
     10332                                        }
     10333                                });
     10334
     10335                                if (!data.hasBlob()) {
     10336                                        data = null;
     10337                                        send();
     10338                                } else {
     10339                                        var blob = data.getBlob();
     10340                                        if (blob.isDetached()) {
     10341                                                attachBlob(blob, function(attachedBlob) {
     10342                                                        blob.destroy();
     10343                                                        appendBlob(blobField, attachedBlob);           
     10344                                                });
     10345                                        } else {
     10346                                                appendBlob(blobField, blob);
     10347                                        }
     10348                                }
     10349                        } else if (data instanceof Blob) {
     10350                                if (data.isDetached()) {
     10351                                        attachBlob(data, function(attachedBlob) {
     10352                                                data.destroy();
     10353                                                data = attachedBlob.uid;
     10354                                                send();
     10355                                        });
     10356                                } else {
     10357                                        data = data.uid;
     10358                                        send();
     10359                                }
     10360                        } else {
     10361                                send();
     10362                        }
     10363                },
     10364
     10365                getResponse: function(responseType) {
     10366                        var frs, blob, self = this.getRuntime();
     10367
     10368                        blob = self.shimExec.call(this, 'XMLHttpRequest', 'getResponseAsBlob');
     10369
     10370                        if (blob) {
     10371                                blob = new File(self.uid, blob);
     10372
     10373                                if ('blob' === responseType) {
     10374                                        return blob;
     10375                                }
     10376
     10377                                try {
     10378                                        frs = new FileReaderSync();
     10379
     10380                                        if (!!~Basic.inArray(responseType, ["", "text"])) {
     10381                                                return frs.readAsText(blob);
     10382                                        } else if ('json' === responseType && !!window.JSON) {
     10383                                                return JSON.parse(frs.readAsText(blob));
     10384                                        }
     10385                                } finally {
     10386                                        blob.destroy();
     10387                                }
     10388                        }
     10389                        return null;
     10390                },
     10391
     10392                abort: function(upload_complete_flag) {
     10393                        var self = this.getRuntime();
     10394
     10395                        self.shimExec.call(this, 'XMLHttpRequest', 'abort');
     10396
     10397                        this.dispatchEvent('readystatechange');
     10398                        // this.dispatchEvent('progress');
     10399                        this.dispatchEvent('abort');
     10400
     10401                        //if (!upload_complete_flag) {
     10402                                // this.dispatchEvent('uploadprogress');
     10403                        //}
     10404                }
     10405        };
     10406
     10407        return (extensions.XMLHttpRequest = XMLHttpRequest);
     10408});
     10409
     10410// Included from: src/javascript/runtime/flash/image/Image.js
     10411
     10412/**
     10413 * Image.js
     10414 *
     10415 * Copyright 2013, Moxiecode Systems AB
     10416 * Released under GPL License.
     10417 *
     10418 * License: http://www.plupload.com/license
     10419 * Contributing: http://www.plupload.com/contributing
     10420 */
     10421
     10422/**
     10423@class moxie/runtime/flash/image/Image
     10424@private
     10425*/
     10426define("moxie/runtime/flash/image/Image", [
     10427        "moxie/runtime/flash/Runtime",
     10428        "moxie/core/utils/Basic",
     10429        "moxie/runtime/Transporter",
     10430        "moxie/file/Blob",
     10431        "moxie/file/FileReaderSync"
     10432], function(extensions, Basic, Transporter, Blob, FileReaderSync) {
     10433       
     10434        var Image = {
     10435                loadFromBlob: function(blob) {
     10436                        var comp = this, self = comp.getRuntime();
     10437
     10438                        function exec(srcBlob) {
     10439                                self.shimExec.call(comp, 'Image', 'loadFromBlob', srcBlob.uid);
     10440                                comp = self = null;
     10441                        }
     10442
     10443                        if (blob.isDetached()) { // binary string
     10444                                var tr = new Transporter();
     10445                                tr.bind("TransportingComplete", function() {
     10446                                        exec(tr.result.getSource());
     10447                                });
     10448                                tr.transport(blob.getSource(), blob.type, { ruid: self.uid });
     10449                        } else {
     10450                                exec(blob.getSource());
     10451                        }
     10452                },
     10453
     10454                loadFromImage: function(img) {
     10455                        var self = this.getRuntime();
     10456                        return self.shimExec.call(this, 'Image', 'loadFromImage', img.uid);
     10457                },
     10458
     10459                getInfo: function() {
     10460                        var self = this.getRuntime()
     10461                        , info = self.shimExec.call(this, 'Image', 'getInfo')
     10462                        ;
     10463
     10464                        if (info.meta && info.meta.thumb && info.meta.thumb.data && !(self.meta.thumb.data instanceof Blob)) {
     10465                                info.meta.thumb.data = new Blob(self.uid, info.meta.thumb.data);
     10466                        }
     10467                        return info;
     10468                },
     10469
     10470                getAsBlob: function(type, quality) {
     10471                        var self = this.getRuntime()
     10472                        , blob = self.shimExec.call(this, 'Image', 'getAsBlob', type, quality)
     10473                        ;
     10474                        if (blob) {
     10475                                return new Blob(self.uid, blob);
     10476                        }
     10477                        return null;
     10478                },
     10479
     10480                getAsDataURL: function() {
     10481                        var self = this.getRuntime()
     10482                        , blob = self.Image.getAsBlob.apply(this, arguments)
     10483                        , frs
     10484                        ;
     10485                        if (!blob) {
     10486                                return null;
     10487                        }
     10488                        frs = new FileReaderSync();
     10489                        return frs.readAsDataURL(blob);
     10490                }
     10491        };
     10492
     10493        return (extensions.Image = Image);
     10494});
     10495
     10496// Included from: src/javascript/runtime/silverlight/Runtime.js
     10497
     10498/**
     10499 * RunTime.js
     10500 *
     10501 * Copyright 2013, Moxiecode Systems AB
     10502 * Released under GPL License.
     10503 *
     10504 * License: http://www.plupload.com/license
     10505 * Contributing: http://www.plupload.com/contributing
     10506 */
     10507
     10508/*global ActiveXObject:true */
     10509
     10510/**
     10511Defines constructor for Silverlight runtime.
     10512
     10513@class moxie/runtime/silverlight/Runtime
     10514@private
     10515*/
    920510516define("moxie/runtime/silverlight/Runtime", [
    9206 ], function() {
    9207         return {};
     10517        "moxie/core/utils/Basic",
     10518        "moxie/core/utils/Env",
     10519        "moxie/core/utils/Dom",
     10520        "moxie/core/Exceptions",
     10521        "moxie/runtime/Runtime"
     10522], function(Basic, Env, Dom, x, Runtime) {
     10523       
     10524        var type = "silverlight", extensions = {};
     10525
     10526        function isInstalled(version) {
     10527                var isVersionSupported = false, control = null, actualVer,
     10528                        actualVerArray, reqVerArray, requiredVersionPart, actualVersionPart, index = 0;
     10529
     10530                try {
     10531                        try {
     10532                                control = new ActiveXObject('AgControl.AgControl');
     10533
     10534                                if (control.IsVersionSupported(version)) {
     10535                                        isVersionSupported = true;
     10536                                }
     10537
     10538                                control = null;
     10539                        } catch (e) {
     10540                                var plugin = navigator.plugins["Silverlight Plug-In"];
     10541
     10542                                if (plugin) {
     10543                                        actualVer = plugin.description;
     10544
     10545                                        if (actualVer === "1.0.30226.2") {
     10546                                                actualVer = "2.0.30226.2";
     10547                                        }
     10548
     10549                                        actualVerArray = actualVer.split(".");
     10550
     10551                                        while (actualVerArray.length > 3) {
     10552                                                actualVerArray.pop();
     10553                                        }
     10554
     10555                                        while ( actualVerArray.length < 4) {
     10556                                                actualVerArray.push(0);
     10557                                        }
     10558
     10559                                        reqVerArray = version.split(".");
     10560
     10561                                        while (reqVerArray.length > 4) {
     10562                                                reqVerArray.pop();
     10563                                        }
     10564
     10565                                        do {
     10566                                                requiredVersionPart = parseInt(reqVerArray[index], 10);
     10567                                                actualVersionPart = parseInt(actualVerArray[index], 10);
     10568                                                index++;
     10569                                        } while (index < reqVerArray.length && requiredVersionPart === actualVersionPart);
     10570
     10571                                        if (requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart)) {
     10572                                                isVersionSupported = true;
     10573                                        }
     10574                                }
     10575                        }
     10576                } catch (e2) {
     10577                        isVersionSupported = false;
     10578                }
     10579
     10580                return isVersionSupported;
     10581        }
     10582
     10583        /**
     10584        Constructor for the Silverlight Runtime
     10585        */
     10586        function SilverlightRuntime(options) {
     10587                var I = this, initTimer;
     10588
     10589                options = Basic.extend({ xap_url: Env.xap_url }, options);
     10590
     10591                Runtime.call(this, options, type, {
     10592                        access_binary: Runtime.capTrue,
     10593                        access_image_binary: Runtime.capTrue,
     10594                        display_media: Runtime.capTest(defined('moxie/image/Image')),
     10595                        do_cors: Runtime.capTrue,
     10596                        drag_and_drop: false,
     10597                        report_upload_progress: Runtime.capTrue,
     10598                        resize_image: Runtime.capTrue,
     10599                        return_response_headers: function(value) {
     10600                                return value && I.mode === 'client';
     10601                        },
     10602                        return_response_type: function(responseType) {
     10603                                if (responseType !== 'json') {
     10604                                        return true;
     10605                                } else {
     10606                                        return !!window.JSON;
     10607                                }
     10608                        },
     10609                        return_status_code: function(code) {
     10610                                return I.mode === 'client' || !Basic.arrayDiff(code, [200, 404]);
     10611                        },
     10612                        select_file: Runtime.capTrue,
     10613                        select_multiple: Runtime.capTrue,
     10614                        send_binary_string: Runtime.capTrue,
     10615                        send_browser_cookies: function(value) {
     10616                                return value && I.mode === 'browser';
     10617                        },
     10618                        send_custom_headers: function(value) {
     10619                                return value && I.mode === 'client';
     10620                        },
     10621                        send_multipart: Runtime.capTrue,
     10622                        slice_blob: Runtime.capTrue,
     10623                        stream_upload: true,
     10624                        summon_file_dialog: false,
     10625                        upload_filesize: Runtime.capTrue,
     10626                        use_http_method: function(methods) {
     10627                                return I.mode === 'client' || !Basic.arrayDiff(methods, ['GET', 'POST']);
     10628                        }
     10629                }, {
     10630                        // capabilities that require specific mode
     10631                        return_response_headers: function(value) {
     10632                                return value ? 'client' : 'browser';
     10633                        },
     10634                        return_status_code: function(code) {
     10635                                return Basic.arrayDiff(code, [200, 404]) ? 'client' : ['client', 'browser'];
     10636                        },
     10637                        send_browser_cookies: function(value) {
     10638                                return value ? 'browser' : 'client';
     10639                        },
     10640                        send_custom_headers: function(value) {
     10641                                return value ? 'client' : 'browser';
     10642                        },
     10643                        use_http_method: function(methods) {
     10644                                return Basic.arrayDiff(methods, ['GET', 'POST']) ? 'client' : ['client', 'browser'];
     10645                        }
     10646                });
     10647
     10648
     10649                // minimal requirement
     10650                if (!isInstalled('2.0.31005.0') || Env.browser === 'Opera') {
     10651                        if (MXI_DEBUG && Env.debug.runtime) {
     10652                                Env.log("\tSilverlight is not installed or minimal version (2.0.31005.0) requirement not met (not likely).");   
     10653                        }
     10654
     10655                        this.mode = false;
     10656                }
     10657
     10658
     10659                Basic.extend(this, {
     10660                        getShim: function() {
     10661                                return Dom.get(this.uid).content.Moxie;
     10662                        },
     10663
     10664                        shimExec: function(component, action) {
     10665                                var args = [].slice.call(arguments, 2);
     10666                                return I.getShim().exec(this.uid, component, action, args);
     10667                        },
     10668
     10669                        init : function() {
     10670                                var container;
     10671
     10672                                container = this.getShimContainer();
     10673
     10674                                container.innerHTML = '<object id="' + this.uid + '" data="data:application/x-silverlight," type="application/x-silverlight-2" width="100%" height="100%" style="outline:none;">' +
     10675                                        '<param name="source" value="' + options.xap_url + '"/>' +
     10676                                        '<param name="background" value="Transparent"/>' +
     10677                                        '<param name="windowless" value="true"/>' +
     10678                                        '<param name="enablehtmlaccess" value="true"/>' +
     10679                                        '<param name="initParams" value="uid=' + this.uid + ',target=' + Runtime.getGlobalEventTarget() + '"/>' +
     10680                                '</object>';
     10681
     10682                                // Init is dispatched by the shim
     10683                                initTimer = setTimeout(function() {
     10684                                        if (I && !I.initialized) { // runtime might be already destroyed by this moment
     10685                                                I.trigger("Error", new x.RuntimeError(x.RuntimeError.NOT_INIT_ERR));
     10686
     10687                                                if (MXI_DEBUG && Env.debug.runtime) {
     10688                                                        Env.log("\Silverlight failed to initialize within a specified period of time (5-10s).");       
     10689                                                }
     10690                                        }
     10691                                }, Env.OS !== 'Windows'? 10000 : 5000); // give it more time to initialize in non Windows OS (like Mac)
     10692                        },
     10693
     10694                        destroy: (function(destroy) { // extend default destroy method
     10695                                return function() {
     10696                                        destroy.call(I);
     10697                                        clearTimeout(initTimer); // initialization check might be still onwait
     10698                                        options = initTimer = destroy = I = null;
     10699                                };
     10700                        }(this.destroy))
     10701
     10702                }, extensions);
     10703        }
     10704
     10705        Runtime.addConstructor(type, SilverlightRuntime);
     10706
     10707        return extensions;
    920810708});
    920910709
     10710// Included from: src/javascript/runtime/silverlight/file/Blob.js
     10711
     10712/**
     10713 * Blob.js
     10714 *
     10715 * Copyright 2013, Moxiecode Systems AB
     10716 * Released under GPL License.
     10717 *
     10718 * License: http://www.plupload.com/license
     10719 * Contributing: http://www.plupload.com/contributing
     10720 */
     10721
     10722/**
     10723@class moxie/runtime/silverlight/file/Blob
     10724@private
     10725*/
     10726define("moxie/runtime/silverlight/file/Blob", [
     10727        "moxie/runtime/silverlight/Runtime",
     10728        "moxie/core/utils/Basic",
     10729        "moxie/runtime/flash/file/Blob"
     10730], function(extensions, Basic, Blob) {
     10731        return (extensions.Blob = Basic.extend({}, Blob));
     10732});
     10733
     10734// Included from: src/javascript/runtime/silverlight/file/FileInput.js
     10735
     10736/**
     10737 * FileInput.js
     10738 *
     10739 * Copyright 2013, Moxiecode Systems AB
     10740 * Released under GPL License.
     10741 *
     10742 * License: http://www.plupload.com/license
     10743 * Contributing: http://www.plupload.com/contributing
     10744 */
     10745
     10746/**
     10747@class moxie/runtime/silverlight/file/FileInput
     10748@private
     10749*/
     10750define("moxie/runtime/silverlight/file/FileInput", [
     10751        "moxie/runtime/silverlight/Runtime",
     10752        "moxie/file/File",
     10753        "moxie/core/utils/Dom",
     10754        "moxie/core/utils/Basic"
     10755], function(extensions, File, Dom, Basic) {
     10756
     10757        function toFilters(accept) {
     10758                var filter = '';
     10759                for (var i = 0; i < accept.length; i++) {
     10760                        filter += (filter !== '' ? '|' : '') + accept[i].title + " | *." + accept[i].extensions.replace(/,/g, ';*.');
     10761                }
     10762                return filter;
     10763        }
     10764
     10765       
     10766        var FileInput = {
     10767                init: function(options) {
     10768                        var comp = this, I = this.getRuntime();
     10769                        var browseButton = Dom.get(options.browse_button);
     10770
     10771                        if (browseButton) {
     10772                                browseButton.setAttribute('tabindex', -1);
     10773                                browseButton = null;
     10774                        }
     10775
     10776                        this.bind("Change", function() {
     10777                                var files = I.shimExec.call(comp, 'FileInput', 'getFiles');
     10778                                comp.files = [];
     10779                                Basic.each(files, function(file) {
     10780                                        comp.files.push(new File(I.uid, file));
     10781                                });
     10782                        }, 999);
     10783                       
     10784                        I.shimExec.call(this, 'FileInput', 'init', toFilters(options.accept), options.multiple);
     10785                        this.trigger('ready');
     10786                },
     10787
     10788                setOption: function(name, value) {
     10789                        if (name == 'accept') {
     10790                                value = toFilters(value);
     10791                        }
     10792                        this.getRuntime().shimExec.call(this, 'FileInput', 'setOption', name, value);
     10793                }
     10794        };
     10795
     10796        return (extensions.FileInput = FileInput);
     10797});
     10798
     10799// Included from: src/javascript/runtime/silverlight/file/FileDrop.js
     10800
     10801/**
     10802 * FileDrop.js
     10803 *
     10804 * Copyright 2013, Moxiecode Systems AB
     10805 * Released under GPL License.
     10806 *
     10807 * License: http://www.plupload.com/license
     10808 * Contributing: http://www.plupload.com/contributing
     10809 */
     10810
     10811/**
     10812@class moxie/runtime/silverlight/file/FileDrop
     10813@private
     10814*/
     10815define("moxie/runtime/silverlight/file/FileDrop", [
     10816        "moxie/runtime/silverlight/Runtime",
     10817        "moxie/core/utils/Dom",
     10818        "moxie/core/utils/Events"
     10819], function(extensions, Dom, Events) {
     10820
     10821        // not exactly useful, since works only in safari (...crickets...)
     10822        var FileDrop = {
     10823                init: function() {
     10824                        var comp = this, self = comp.getRuntime(), dropZone;
     10825
     10826                        dropZone = self.getShimContainer();
     10827
     10828                        Events.addEvent(dropZone, 'dragover', function(e) {
     10829                                e.preventDefault();
     10830                                e.stopPropagation();
     10831                                e.dataTransfer.dropEffect = 'copy';
     10832                        }, comp.uid);
     10833
     10834                        Events.addEvent(dropZone, 'dragenter', function(e) {
     10835                                e.preventDefault();
     10836                                var flag = Dom.get(self.uid).dragEnter(e);
     10837                                // If handled, then stop propagation of event in DOM
     10838                                if (flag) {
     10839                                        e.stopPropagation();
     10840                                }
     10841                        }, comp.uid);
     10842
     10843                        Events.addEvent(dropZone, 'drop', function(e) {
     10844                                e.preventDefault();
     10845                                var flag = Dom.get(self.uid).dragDrop(e);
     10846                                // If handled, then stop propagation of event in DOM
     10847                                if (flag) {
     10848                                        e.stopPropagation();
     10849                                }
     10850                        }, comp.uid);
     10851
     10852                        return self.shimExec.call(this, 'FileDrop', 'init');
     10853                }
     10854        };
     10855
     10856        return (extensions.FileDrop = FileDrop);
     10857});
     10858
     10859// Included from: src/javascript/runtime/silverlight/file/FileReader.js
     10860
     10861/**
     10862 * FileReader.js
     10863 *
     10864 * Copyright 2013, Moxiecode Systems AB
     10865 * Released under GPL License.
     10866 *
     10867 * License: http://www.plupload.com/license
     10868 * Contributing: http://www.plupload.com/contributing
     10869 */
     10870
     10871/**
     10872@class moxie/runtime/silverlight/file/FileReader
     10873@private
     10874*/
     10875define("moxie/runtime/silverlight/file/FileReader", [
     10876        "moxie/runtime/silverlight/Runtime",
     10877        "moxie/core/utils/Basic",
     10878        "moxie/runtime/flash/file/FileReader"
     10879], function(extensions, Basic, FileReader) {
     10880        return (extensions.FileReader = Basic.extend({}, FileReader));
     10881});
     10882
     10883// Included from: src/javascript/runtime/silverlight/file/FileReaderSync.js
     10884
     10885/**
     10886 * FileReaderSync.js
     10887 *
     10888 * Copyright 2013, Moxiecode Systems AB
     10889 * Released under GPL License.
     10890 *
     10891 * License: http://www.plupload.com/license
     10892 * Contributing: http://www.plupload.com/contributing
     10893 */
     10894
     10895/**
     10896@class moxie/runtime/silverlight/file/FileReaderSync
     10897@private
     10898*/
     10899define("moxie/runtime/silverlight/file/FileReaderSync", [
     10900        "moxie/runtime/silverlight/Runtime",
     10901        "moxie/core/utils/Basic",
     10902        "moxie/runtime/flash/file/FileReaderSync"
     10903], function(extensions, Basic, FileReaderSync) {
     10904        return (extensions.FileReaderSync = Basic.extend({}, FileReaderSync));
     10905});
     10906
     10907// Included from: src/javascript/runtime/silverlight/runtime/Transporter.js
     10908
     10909/**
     10910 * Transporter.js
     10911 *
     10912 * Copyright 2013, Moxiecode Systems AB
     10913 * Released under GPL License.
     10914 *
     10915 * License: http://www.plupload.com/license
     10916 * Contributing: http://www.plupload.com/contributing
     10917 */
     10918
     10919/**
     10920@class moxie/runtime/silverlight/runtime/Transporter
     10921@private
     10922*/
     10923define("moxie/runtime/silverlight/runtime/Transporter", [
     10924        "moxie/runtime/silverlight/Runtime",
     10925        "moxie/core/utils/Basic",
     10926        "moxie/runtime/flash/runtime/Transporter"
     10927], function(extensions, Basic, Transporter) {
     10928        return (extensions.Transporter = Basic.extend({}, Transporter));
     10929});
     10930
     10931// Included from: src/javascript/runtime/silverlight/xhr/XMLHttpRequest.js
     10932
     10933/**
     10934 * XMLHttpRequest.js
     10935 *
     10936 * Copyright 2013, Moxiecode Systems AB
     10937 * Released under GPL License.
     10938 *
     10939 * License: http://www.plupload.com/license
     10940 * Contributing: http://www.plupload.com/contributing
     10941 */
     10942
     10943/**
     10944@class moxie/runtime/silverlight/xhr/XMLHttpRequest
     10945@private
     10946*/
     10947define("moxie/runtime/silverlight/xhr/XMLHttpRequest", [
     10948        "moxie/runtime/silverlight/Runtime",
     10949        "moxie/core/utils/Basic",
     10950        "moxie/runtime/flash/xhr/XMLHttpRequest",
     10951        "moxie/runtime/silverlight/file/FileReaderSync",
     10952        "moxie/runtime/silverlight/runtime/Transporter"
     10953], function(extensions, Basic, XMLHttpRequest, FileReaderSyncSilverlight, TransporterSilverlight) {
     10954        return (extensions.XMLHttpRequest = Basic.extend({}, XMLHttpRequest));
     10955});
     10956
     10957// Included from: src/javascript/runtime/silverlight/image/Image.js
     10958
     10959/**
     10960 * Image.js
     10961 *
     10962 * Copyright 2013, Moxiecode Systems AB
     10963 * Released under GPL License.
     10964 *
     10965 * License: http://www.plupload.com/license
     10966 * Contributing: http://www.plupload.com/contributing
     10967 */
     10968 
     10969/**
     10970@class moxie/runtime/silverlight/image/Image
     10971@private
     10972*/
     10973define("moxie/runtime/silverlight/image/Image", [
     10974        "moxie/runtime/silverlight/Runtime",
     10975        "moxie/core/utils/Basic",
     10976        "moxie/file/Blob",
     10977        "moxie/runtime/flash/image/Image"
     10978], function(extensions, Basic, Blob, Image) {
     10979        return (extensions.Image = Basic.extend({}, Image, {
     10980
     10981                getInfo: function() {
     10982                        var self = this.getRuntime()
     10983                        , grps = ['tiff', 'exif', 'gps', 'thumb']
     10984                        , info = { meta: {} }
     10985                        , rawInfo = self.shimExec.call(this, 'Image', 'getInfo')
     10986                        ;
     10987
     10988                        if (rawInfo.meta) {
     10989                                Basic.each(grps, function(grp) {
     10990                                        var meta = rawInfo.meta[grp]
     10991                                        , tag
     10992                                        , i
     10993                                        , length
     10994                                        , value
     10995                                        ;
     10996                                        if (meta && meta.keys) {
     10997                                                info.meta[grp] = {};
     10998                                                for (i = 0, length = meta.keys.length; i < length; i++) {
     10999                                                        tag = meta.keys[i];
     11000                                                        value = meta[tag];
     11001                                                        if (value) {
     11002                                                                // convert numbers
     11003                                                                if (/^(\d|[1-9]\d+)$/.test(value)) { // integer (make sure doesn't start with zero)
     11004                                                                        value = parseInt(value, 10);
     11005                                                                } else if (/^\d*\.\d+$/.test(value)) { // double
     11006                                                                        value = parseFloat(value);
     11007                                                                }
     11008                                                                info.meta[grp][tag] = value;
     11009                                                        }
     11010                                                }
     11011                                        }
     11012                                });
     11013
     11014                                // save thumb data as blob
     11015                                if (info.meta && info.meta.thumb && info.meta.thumb.data && !(self.meta.thumb.data instanceof Blob)) {
     11016                                        info.meta.thumb.data = new Blob(self.uid, info.meta.thumb.data);
     11017                                }
     11018                        }
     11019
     11020                        info.width = parseInt(rawInfo.width, 10);
     11021                        info.height = parseInt(rawInfo.height, 10);
     11022                        info.size = parseInt(rawInfo.size, 10);
     11023                        info.type = rawInfo.type;
     11024                        info.name = rawInfo.name;
     11025
     11026                        return info;
     11027                },
     11028
     11029                resize: function(rect, ratio, opts) {
     11030                        this.getRuntime().shimExec.call(this, 'Image', 'resize', rect.x, rect.y, rect.width, rect.height, ratio, opts.preserveHeaders, opts.resample);
     11031                }
     11032        }));
     11033});
     11034
    921011035// Included from: src/javascript/runtime/html4/Runtime.js
    921111036
    921211037/**
     
    924511070                Runtime.call(this, options, type, {
    924611071                        access_binary: Test(window.FileReader || window.File && File.getAsDataURL),
    924711072                        access_image_binary: false,
    9248                         display_media: Test(extensions.Image && (Env.can('create_canvas') || Env.can('use_data_uri_over32kb'))),
     11073                        display_media: Test(
     11074                                (Env.can('create_canvas') || Env.can('use_data_uri_over32kb')) &&
     11075                                defined('moxie/image/Image')
     11076                        ),
    924911077                        do_cors: false,
    925011078                        drag_and_drop: false,
    925111079                        filter_by_extension: Test(function() { // if you know how to feature-detect this, please suggest
    9252                                 return (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '>=')) ||
    9253                                         (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
    9254                                         (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '>='));
     11080                                return !(
     11081                                        (Env.browser === 'Chrome' && Env.verComp(Env.version, 28, '<')) ||
     11082                                        (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<')) ||
     11083                                        (Env.browser === 'Safari' && Env.verComp(Env.version, 7, '<')) ||
     11084                                        (Env.browser === 'Firefox' && Env.verComp(Env.version, 37, '<'))
     11085                                );
    925511086                        }()),
    925611087                        resize_image: function() {
    925711088                                return extensions.Image && I.can('access_binary') && Env.can('create_canvas');
     
    927911110                                return I.can('select_file');
    928011111                        },
    928111112                        summon_file_dialog: function() { // yeah... some dirty sniffing here...
    9282                                 return I.can('select_file') && (
    9283                                         (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '>=')) ||
    9284                                         (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '>=')) ||
    9285                                         (Env.browser === 'IE' && Env.verComp(Env.version, 10, '>=')) ||
    9286                                         !!~Basic.inArray(Env.browser, ['Chrome', 'Safari'])
     11113                                return I.can('select_file') && !(
     11114                                        (Env.browser === 'Firefox' && Env.verComp(Env.version, 4, '<')) ||
     11115                                        (Env.browser === 'Opera' && Env.verComp(Env.version, 12, '<')) ||
     11116                                        (Env.browser === 'IE' && Env.verComp(Env.version, 10, '<'))
    928711117                                );
    928811118                        },
    928911119                        upload_filesize: True,
     
    934111171], function(extensions, File, Basic, Dom, Events, Mime, Env) {
    934211172       
    934311173        function FileInput() {
    9344                 var _uid, _mimes = [], _options;
     11174                var _uid, _mimes = [], _options, _browseBtnZIndex; // save original z-index;
    934511175
    934611176                function addInput() {
    934711177                        var comp = this, I = comp.getRuntime(), shimContainer, browseButton, currForm, form, input, uid;
     
    934811178
    934911179                        uid = Basic.guid('uid_');
    935011180
    9351                         shimContainer = I.getShimContainer(); // we get new ref everytime to avoid memory leaks in IE
     11181                        shimContainer = I.getShimContainer(); // we get new ref every time to avoid memory leaks in IE
    935211182
    935311183                        if (_uid) { // move previous form out of the view
    935411184                                currForm = Dom.get(_uid + '_form');
    935511185                                if (currForm) {
    935611186                                        Basic.extend(currForm.style, { top: '100%' });
     11187                                        // it shouldn't be possible to tab into the hidden element
     11188                                        currForm.firstChild.setAttribute('tabindex', -1);
    935711189                                }
    935811190                        }
    935911191
     
    937611208                        input = document.createElement('input');
    937711209                        input.setAttribute('id', uid);
    937811210                        input.setAttribute('type', 'file');
    9379                         input.setAttribute('name', _options.name || 'Filedata');
    938011211                        input.setAttribute('accept', _mimes.join(','));
    938111212
     11213                        if (I.can('summon_file_dialog')) {
     11214                                input.setAttribute('tabindex', -1);
     11215                        }
     11216
    938211217                        Basic.extend(input.style, {
    938311218                                fontSize: '999px',
    938411219                                opacity: 0
     
    941111246
    941211247                                if (this.files) { // check if browser is fresh enough
    941311248                                        file = this.files[0];
    9414 
    9415                                         // ignore empty files (IE10 for example hangs if you try to send them via XHR)
    9416                                         if (file.size === 0) {
    9417                                                 form.parentNode.removeChild(form);
    9418                                                 return;
    9419                                         }
    942011249                                } else {
    942111250                                        file = {
    942211251                                                name: this.value
     
    946411293
    946511294                                // figure out accept string
    946611295                                _options = options;
    9467                                 _mimes = options.accept.mimes || Mime.extList2mimes(options.accept, I.can('filter_by_extension'));
     11296                                _mimes = Mime.extList2mimes(options.accept, I.can('filter_by_extension'));
    946811297
    946911298                                shimContainer = I.getShimContainer();
    947011299
     
    947211301                                        var browseButton, zIndex, top;
    947311302
    947411303                                        browseButton = Dom.get(options.browse_button);
     11304                                        _browseBtnZIndex = Dom.getStyle(browseButton, 'z-index') || 'auto';
    947511305
    947611306                                        // Route click event to the input[type=file] element for browsers that support such behavior
    947711307                                        if (I.can('summon_file_dialog')) {
    947811308                                                if (Dom.getStyle(browseButton, 'position') === 'static') {
    947911309                                                        browseButton.style.position = 'relative';
    9480                                                 }
     11310                                                }                                               
    948111311
    9482                                                 zIndex = parseInt(Dom.getStyle(browseButton, 'z-index'), 10) || 1;
     11312                                                comp.bind('Refresh', function() {
     11313                                                        zIndex = parseInt(_browseBtnZIndex, 10) || 1;
    948311314
    9484                                                 browseButton.style.zIndex = zIndex;
    9485                                                 shimContainer.style.zIndex = zIndex - 1;
     11315                                                        Dom.get(_options.browse_button).style.zIndex = zIndex;
     11316                                                        this.getRuntime().getShimContainer().style.zIndex = zIndex - 1;
     11317                                                });
     11318                                        } else {
     11319                                                // it shouldn't be possible to tab into the hidden element
     11320                                                browseButton.setAttribute('tabindex', -1);
    948611321                                        }
    948711322
    948811323                                        /* Since we have to place input[type=file] on top of the browse_button for some browsers,
     
    951911354                                });
    952011355                        },
    952111356
     11357                        setOption: function(name, value) {
     11358                                var I = this.getRuntime();
     11359                                var input;
    952211360
     11361                                if (name == 'accept') {
     11362                                        _mimes = value.mimes || Mime.extList2mimes(value, I.can('filter_by_extension'));
     11363                                }
     11364
     11365                                // update current input
     11366                                input = Dom.get(_uid)
     11367                                if (input) {
     11368                                        input.setAttribute('accept', _mimes.join(','));
     11369                                }
     11370                        },
     11371
     11372
    952311373                        disable: function(state) {
    952411374                                var input;
    952511375
     
    953211382                                var I = this.getRuntime()
    953311383                                , shim = I.getShim()
    953411384                                , shimContainer = I.getShimContainer()
     11385                                , container = _options && Dom.get(_options.container)
     11386                                , browseButton = _options && Dom.get(_options.browse_button)
    953511387                                ;
    953611388                               
    9537                                 Events.removeAllEvents(shimContainer, this.uid);
    9538                                 Events.removeAllEvents(_options && Dom.get(_options.container), this.uid);
    9539                                 Events.removeAllEvents(_options && Dom.get(_options.browse_button), this.uid);
     11389                                if (container) {
     11390                                        Events.removeAllEvents(container, this.uid);
     11391                                }
    954011392                               
     11393                                if (browseButton) {
     11394                                        Events.removeAllEvents(browseButton, this.uid);
     11395                                        browseButton.style.zIndex = _browseBtnZIndex; // reset to original value
     11396                                }
     11397                               
    954111398                                if (shimContainer) {
     11399                                        Events.removeAllEvents(shimContainer, this.uid);
    954211400                                        shimContainer.innerHTML = '';
    954311401                                }
    954411402
    954511403                                shim.removeInstance(this.uid);
    954611404
    9547                                 _uid = _mimes = _options = shimContainer = shim = null;
     11405                                _uid = _mimes = _options = shimContainer = container = browseButton = shim = null;
    954811406                        }
    954911407                });
    955011408        }
     
    981711675                                        // target.dispatchEvent('readystatechange');
    981811676                                        target.dispatchEvent('abort');
    981911677                                });
     11678                        },
     11679
     11680                        destroy: function() {
     11681                                this.getRuntime().getShim().removeInstance(this.uid);
    982011682                        }
    982111683                });
    982211684        }
     
    984711709        return (extensions.Image = Image);
    984811710});
    984911711
    9850 expose(["moxie/core/utils/Basic","moxie/core/utils/Env","moxie/core/I18n","moxie/core/utils/Mime","moxie/core/utils/Dom","moxie/core/Exceptions","moxie/core/EventTarget","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/FileInput","moxie/core/utils/Encode","moxie/file/Blob","moxie/file/File","moxie/file/FileDrop","moxie/file/FileReader","moxie/core/utils/Url","moxie/runtime/RuntimeTarget","moxie/file/FileReaderSync","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/runtime/Transporter","moxie/image/Image","moxie/core/utils/Events"]);
     11712expose(["moxie/core/utils/Basic","moxie/core/utils/Encode","moxie/core/utils/Env","moxie/core/Exceptions","moxie/core/utils/Dom","moxie/core/EventTarget","moxie/runtime/Runtime","moxie/runtime/RuntimeClient","moxie/file/Blob","moxie/core/I18n","moxie/core/utils/Mime","moxie/file/FileInput","moxie/file/File","moxie/file/FileDrop","moxie/file/FileReader","moxie/core/utils/Url","moxie/runtime/RuntimeTarget","moxie/xhr/FormData","moxie/xhr/XMLHttpRequest","moxie/image/Image","moxie/core/utils/Events","moxie/runtime/html5/image/ResizerCanvas"]);
    985111713})(this);
    9852 /**
    9853  * o.js
    9854  *
    9855  * Copyright 2013, Moxiecode Systems AB
    9856  * Released under GPL License.
    9857  *
    9858  * License: http://www.plupload.com/license
    9859  * Contributing: http://www.plupload.com/contributing
    9860  */
    9861 
    9862 /*global moxie:true */
    9863 
    9864 /**
    9865 Globally exposed namespace with the most frequently used public classes and handy methods.
    9866 
    9867 @class o
    9868 @static
    9869 @private
    9870 */
    9871 (function(exports) {
    9872         "use strict";
    9873 
    9874         var o = {}, inArray = exports.moxie.core.utils.Basic.inArray;
    9875 
    9876         // directly add some public classes
    9877         // (we do it dynamically here, since for custom builds we cannot know beforehand what modules were included)
    9878         (function addAlias(ns) {
    9879                 var name, itemType;
    9880                 for (name in ns) {
    9881                         itemType = typeof(ns[name]);
    9882                         if (itemType === 'object' && !~inArray(name, ['Exceptions', 'Env', 'Mime'])) {
    9883                                 addAlias(ns[name]);
    9884                         } else if (itemType === 'function') {
    9885                                 o[name] = ns[name];
    9886                         }
    9887                 }
    9888         })(exports.moxie);
    9889 
    9890         // add some manually
    9891         o.Env = exports.moxie.core.utils.Env;
    9892         o.Mime = exports.moxie.core.utils.Mime;
    9893         o.Exceptions = exports.moxie.core.Exceptions;
    9894 
    9895         // expose globally
    9896         exports.mOxie = o;
    9897         if (!exports.o) {
    9898                 exports.o = o;
    9899         }
    9900         return o;
    9901 })(this);
     11714}));
     11715 No newline at end of file
  • src/js/_enqueues/vendor/plupload/plupload.js

     
    11/**
    22 * Plupload - multi-runtime File Uploader
    3  * v2.1.9
     3 * v2.3.6
    44 *
    55 * Copyright 2013, Moxiecode Systems AB
    66 * Released under GPL License.
     
    88 * License: http://www.plupload.com/license
    99 * Contributing: http://www.plupload.com/contributing
    1010 *
    11  * Date: 2016-05-15
     11 * Date: 2017-11-03
    1212 */
    13 /**
    14  * Plupload.js
    15  *
    16  * Copyright 2013, Moxiecode Systems AB
    17  * Released under GPL License.
    18  *
    19  * License: http://www.plupload.com/license
    20  * Contributing: http://www.plupload.com/contributing
    21  */
     13;
     14(function(global, factory) {
     15        var extract = function() {
     16                var ctx = {};
     17                factory.apply(ctx, arguments);
     18                return ctx.plupload;
     19        };
    2220
    23 /**
    24  * Modified for WordPress, Silverlight and Flash runtimes support was removed.
    25  * See https://core.trac.wordpress.org/ticket/41755.
    26  */
     21        if (typeof define === "function" && define.amd) {
     22                define("plupload", ['./moxie'], extract);
     23        } else if (typeof module === "object" && module.exports) {
     24                module.exports = extract(require('./moxie'));
     25        } else {
     26                global.plupload = extract(global.moxie);
     27        }
     28}(this || window, function(moxie) {
     29        /**
     30         * Plupload.js
     31         *
     32         * Copyright 2013, Moxiecode Systems AB
     33         * Released under GPL License.
     34         *
     35         * License: http://www.plupload.com/license
     36         * Contributing: http://www.plupload.com/contributing
     37         */
    2738
    28 /*global mOxie:true */
     39        ;
     40        (function(exports, o, undef) {
    2941
    30 ;(function(window, o, undef) {
     42                var delay = window.setTimeout;
     43                var fileFilters = {};
     44                var u = o.core.utils;
     45                var Runtime = o.runtime.Runtime;
    3146
    32 var delay = window.setTimeout
    33 , fileFilters = {}
    34 ;
     47                // convert plupload features to caps acceptable by mOxie
     48                function normalizeCaps(settings) {
     49                        var features = settings.required_features,
     50                                caps = {};
    3551
    36 // convert plupload features to caps acceptable by mOxie
    37 function normalizeCaps(settings) {             
    38         var features = settings.required_features, caps = {};
     52                        function resolve(feature, value, strict) {
     53                                // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
     54                                var map = {
     55                                        chunks: 'slice_blob',
     56                                        jpgresize: 'send_binary_string',
     57                                        pngresize: 'send_binary_string',
     58                                        progress: 'report_upload_progress',
     59                                        multi_selection: 'select_multiple',
     60                                        dragdrop: 'drag_and_drop',
     61                                        drop_element: 'drag_and_drop',
     62                                        headers: 'send_custom_headers',
     63                                        urlstream_upload: 'send_binary_string',
     64                                        canSendBinary: 'send_binary',
     65                                        triggerDialog: 'summon_file_dialog'
     66                                };
    3967
    40         function resolve(feature, value, strict) {
    41                 // Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
    42                 var map = {
    43                         chunks: 'slice_blob',
    44                         jpgresize: 'send_binary_string',
    45                         pngresize: 'send_binary_string',
    46                         progress: 'report_upload_progress',
    47                         multi_selection: 'select_multiple',
    48                         dragdrop: 'drag_and_drop',
    49                         drop_element: 'drag_and_drop',
    50                         headers: 'send_custom_headers',
    51                         urlstream_upload: 'send_binary_string',
    52                         canSendBinary: 'send_binary',
    53                         triggerDialog: 'summon_file_dialog'
    54                 };
     68                                if (map[feature]) {
     69                                        caps[map[feature]] = value;
     70                                } else if (!strict) {
     71                                        caps[feature] = value;
     72                                }
     73                        }
    5574
    56                 if (map[feature]) {
    57                         caps[map[feature]] = value;
    58                 } else if (!strict) {
    59                         caps[feature] = value;
    60                 }
    61         }
     75                        if (typeof(features) === 'string') {
     76                                plupload.each(features.split(/\s*,\s*/), function(feature) {
     77                                        resolve(feature, true);
     78                                });
     79                        } else if (typeof(features) === 'object') {
     80                                plupload.each(features, function(value, feature) {
     81                                        resolve(feature, value);
     82                                });
     83                        } else if (features === true) {
     84                                // check settings for required features
     85                                if (settings.chunk_size && settings.chunk_size > 0) {
     86                                        caps.slice_blob = true;
     87                                }
    6288
    63         if (typeof(features) === 'string') {
    64                 plupload.each(features.split(/\s*,\s*/), function(feature) {
    65                         resolve(feature, true);
    66                 });
    67         } else if (typeof(features) === 'object') {
    68                 plupload.each(features, function(value, feature) {
    69                         resolve(feature, value);
    70                 });
    71         } else if (features === true) {
    72                 // check settings for required features
    73                 if (settings.chunk_size > 0) {
    74                         caps.slice_blob = true;
    75                 }
     89                                if (!plupload.isEmptyObj(settings.resize) || settings.multipart === false) {
     90                                        caps.send_binary_string = true;
     91                                }
    7692
    77                 if (settings.resize.enabled || !settings.multipart) {
    78                         caps.send_binary_string = true;
     93                                if (settings.http_method) {
     94                                        caps.use_http_method = settings.http_method;
     95                                }
     96
     97                                plupload.each(settings, function(value, feature) {
     98                                        resolve(feature, !!value, true); // strict check
     99                                });
     100                        }
     101
     102                        return caps;
    79103                }
    80                
    81                 plupload.each(settings, function(value, feature) {
    82                         resolve(feature, !!value, true); // strict check
    83                 });
    84         }
    85104
    86         // WP: only html runtimes.
    87         settings.runtimes = 'html5,html4';
     105                /**
     106                 * @module plupload
     107                 * @static
     108                 */
     109                var plupload = {
     110                        /**
     111                         * Plupload version will be replaced on build.
     112                         *
     113                         * @property VERSION
     114                         * @for Plupload
     115                         * @static
     116                         * @final
     117                         */
     118                        VERSION: '2.3.6',
    88119
    89         return caps;
    90 }
     120                        /**
     121                         * The state of the queue before it has started and after it has finished
     122                         *
     123                         * @property STOPPED
     124                         * @static
     125                         * @final
     126                         */
     127                        STOPPED: 1,
    91128
    92 /**
    93  * @module plupload     
    94  * @static
    95  */
    96 var plupload = {
    97         /**
    98          * Plupload version will be replaced on build.
    99          *
    100          * @property VERSION
    101          * @for Plupload
    102          * @static
    103          * @final
    104          */
    105         VERSION : '2.1.9',
     129                        /**
     130                         * Upload process is running
     131                         *
     132                         * @property STARTED
     133                         * @static
     134                         * @final
     135                         */
     136                        STARTED: 2,
    106137
    107         /**
    108          * The state of the queue before it has started and after it has finished
    109         *
    110          * @property STOPPED
    111         * @static
    112         * @final
    113         */
    114         STOPPED : 1,
     138                        /**
     139                         * File is queued for upload
     140                        *
     141                         * @property QUEUED
     142                        * @static
     143                        * @final
     144                        */
     145                        QUEUED: 1,
    115146
    116         /**
    117          * Upload process is running
    118         *
    119          * @property STARTED
    120         * @static
    121         * @final
    122         */
    123         STARTED : 2,
     147                        /**
     148                         * File is being uploaded
     149                        *
     150                         * @property UPLOADING
     151                        * @static
     152                        * @final
     153                        */
     154                        UPLOADING: 2,
    124155
    125         /**
    126          * File is queued for upload
    127         *
    128          * @property QUEUED
    129         * @static
    130         * @final
    131         */
    132         QUEUED : 1,
     156                        /**
     157                         * File has failed to be uploaded
     158                        *
     159                         * @property FAILED
     160                        * @static
     161                        * @final
     162                        */
     163                        FAILED: 4,
    133164
    134         /**
    135          * File is being uploaded
    136         *
    137          * @property UPLOADING
    138         * @static
    139         * @final
    140         */
    141         UPLOADING : 2,
     165                        /**
     166                         * File has been uploaded successfully
     167                        *
     168                         * @property DONE
     169                        * @static
     170                        * @final
     171                        */
     172                        DONE: 5,
    142173
    143         /**
    144          * File has failed to be uploaded
    145          *
    146          * @property FAILED
    147          * @static
    148          * @final
    149          */
    150         FAILED : 4,
     174                        // Error constants used by the Error event
    151175
    152         /**
    153          * File has been uploaded successfully
    154         *
    155          * @property DONE
    156         * @static
    157         * @final
    158         */
    159         DONE : 5,
     176                        /**
     177                         * Generic error for example if an exception is thrown inside Silverlight.
     178                        *
     179                         * @property GENERIC_ERROR
     180                        * @static
     181                        * @final
     182                        */
     183                        GENERIC_ERROR: -100,
    160184
    161         // Error constants used by the Error event
     185                        /**
     186                         * HTTP transport error. For example if the server produces a HTTP status other than 200.
     187                         *
     188                         * @property HTTP_ERROR
     189                         * @static
     190                         * @final
     191                         */
     192                        HTTP_ERROR: -200,
    162193
    163         /**
    164          * Generic error for example if an exception is thrown inside Silverlight.
    165         *
    166          * @property GENERIC_ERROR
    167         * @static
    168         * @final
    169         */
    170         GENERIC_ERROR : -100,
     194                        /**
     195                         * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
     196                        *
     197                         * @property IO_ERROR
     198                        * @static
     199                        * @final
     200                        */
     201                        IO_ERROR: -300,
    171202
    172         /**
    173          * HTTP transport error. For example if the server produces a HTTP status other than 200.
    174          *
    175          * @property HTTP_ERROR
    176          * @static
    177          * @final
    178          */
    179         HTTP_ERROR : -200,
     203                        /**
     204                         * @property SECURITY_ERROR
     205                         * @static
     206                         * @final
     207                         */
     208                        SECURITY_ERROR: -400,
    180209
    181         /**
    182          * Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
    183         *
    184          * @property IO_ERROR
    185         * @static
    186         * @final
    187         */
    188         IO_ERROR : -300,
     210                        /**
     211                         * Initialization error. Will be triggered if no runtime was initialized.
     212                        *
     213                         * @property INIT_ERROR
     214                        * @static
     215                        * @final
     216                        */
     217                        INIT_ERROR: -500,
    189218
    190         /**
    191          * @property SECURITY_ERROR
    192          * @static
    193          * @final
    194          */
    195         SECURITY_ERROR : -400,
     219                        /**
     220                         * File size error. If the user selects a file that is too large or is empty it will be blocked and
     221                         * an error of this type will be triggered.
     222                         *
     223                         * @property FILE_SIZE_ERROR
     224                         * @static
     225                         * @final
     226                         */
     227                        FILE_SIZE_ERROR: -600,
    196228
    197         /**
    198          * Initialization error. Will be triggered if no runtime was initialized.
    199         *
    200          * @property INIT_ERROR
    201         * @static
    202         * @final
    203         */
    204         INIT_ERROR : -500,
     229                        /**
     230                         * File extension error. If the user selects a file that isn't valid according to the filters setting.
     231                        *
     232                         * @property FILE_EXTENSION_ERROR
     233                        * @static
     234                        * @final
     235                        */
     236                        FILE_EXTENSION_ERROR: -601,
    205237
    206         /**
    207          * File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
    208         *
    209          * @property FILE_SIZE_ERROR
    210         * @static
    211         * @final
    212         */
    213         FILE_SIZE_ERROR : -600,
     238                        /**
     239                         * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
     240                        *
     241                         * @property FILE_DUPLICATE_ERROR
     242                        * @static
     243                        * @final
     244                        */
     245                        FILE_DUPLICATE_ERROR: -602,
    214246
    215         /**
    216          * File extension error. If the user selects a file that isn't valid according to the filters setting.
    217         *
    218          * @property FILE_EXTENSION_ERROR
    219         * @static
    220         * @final
    221         */
    222         FILE_EXTENSION_ERROR : -601,
     247                        /**
     248                         * Runtime will try to detect if image is proper one. Otherwise will throw this error.
     249                        *
     250                         * @property IMAGE_FORMAT_ERROR
     251                        * @static
     252                        * @final
     253                        */
     254                        IMAGE_FORMAT_ERROR: -700,
    223255
    224         /**
    225          * Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
    226          *
    227          * @property FILE_DUPLICATE_ERROR
    228          * @static
    229          * @final
    230          */
    231         FILE_DUPLICATE_ERROR : -602,
     256                        /**
     257                         * While working on files runtime may run out of memory and will throw this error.
     258                         *
     259                         * @since 2.1.2
     260                         * @property MEMORY_ERROR
     261                         * @static
     262                         * @final
     263                         */
     264                        MEMORY_ERROR: -701,
    232265
    233         /**
    234          * Runtime will try to detect if image is proper one. Otherwise will throw this error.
    235         *
    236          * @property IMAGE_FORMAT_ERROR
    237         * @static
    238         * @final
    239         */
    240         IMAGE_FORMAT_ERROR : -700,
     266                        /**
     267                         * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
     268                        *
     269                         * @property IMAGE_DIMENSIONS_ERROR
     270                        * @static
     271                        * @final
     272                        */
     273                        IMAGE_DIMENSIONS_ERROR: -702,
    241274
    242         /**
    243          * While working on files runtime may run out of memory and will throw this error.
    244          *
    245          * @since 2.1.2
    246          * @property MEMORY_ERROR
    247          * @static
    248          * @final
    249          */
    250         MEMORY_ERROR : -701,
     275                        /**
     276                         * Expose whole moxie (#1469).
     277                         *
     278                         * @property moxie
     279                         * @type Object
     280                         * @final
     281                         */
     282                        moxie: o,
    251283
    252         /**
    253          * Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
    254         *
    255          * @property IMAGE_DIMENSIONS_ERROR
    256          * @static
    257         * @final
    258         */
    259         IMAGE_DIMENSIONS_ERROR : -702,
     284                        /**
     285                         * Mime type lookup table.
     286                        *
     287                         * @property mimeTypes
     288                         * @type Object
     289                        * @final
     290                        */
     291                        mimeTypes: u.Mime.mimes,
    260292
    261         /**
    262          * Mime type lookup table.
    263          *
    264          * @property mimeTypes
    265          * @type Object
    266          * @final
    267          */
    268         mimeTypes : o.mimes,
     293                        /**
     294                         * In some cases sniffing is the only way around :(
     295                         */
     296                        ua: u.Env,
    269297
    270         /**
    271          * In some cases sniffing is the only way around :(
    272          */
    273         ua: o.ua,
     298                        /**
     299                         * Gets the true type of the built-in object (better version of typeof).
     300                         * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
     301                         *
     302                         * @method typeOf
     303                         * @static
     304                         * @param {Object} o Object to check.
     305                         * @return {String} Object [[Class]]
     306                         */
     307                        typeOf: u.Basic.typeOf,
    274308
    275         /**
    276          * Gets the true type of the built-in object (better version of typeof).
    277          * @credits Angus Croll (http://javascriptweblog.wordpress.com/)
    278          *
    279          * @method typeOf
    280          * @static
    281          * @param {Object} o Object to check.
    282          * @return {String} Object [[Class]]
    283         */
    284         typeOf: o.typeOf,
     309                        /**
     310                         * Extends the specified object with another object.
     311                         *
     312                         * @method extend
     313                         * @static
     314                         * @param {Object} target Object to extend.
     315                         * @param {Object..} obj Multiple objects to extend with.
     316                         * @return {Object} Same as target, the extended object.
     317                        */
     318                        extend: u.Basic.extend,
    285319
    286         /**
    287          * Extends the specified object with another object.
    288          *
    289          * @method extend
    290          * @static
    291          * @param {Object} target Object to extend.
    292          * @param {Object..} obj Multiple objects to extend with.
    293          * @return {Object} Same as target, the extended object.
    294          */
    295         extend : o.extend,
     320                        /**
     321                         * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
     322                         * The only way a user would be able to get the same ID is if the two persons at the same exact millisecond manages
     323                         * 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.
     324                         * 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
     325                         * to an user unique key.
     326                         *
     327                         * @method guid
     328                         * @static
     329                         * @return {String} Virtually unique id.
     330                         */
     331                        guid: u.Basic.guid,
    296332
    297         /**
    298          * Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
    299          * The only way a user would be able to get the same ID is if the two persons at the same exact millisecond manages
    300          * 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.
    301          * 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
    302          * to an user unique key.
    303          *
    304          * @method guid
    305          * @static
    306          * @return {String} Virtually unique id.
    307          */
    308         guid : o.guid,
     333                        /**
     334                         * Get array of DOM Elements by their ids.
     335                         *
     336                         * @method get
     337                         * @param {String} id Identifier of the DOM Element
     338                         * @return {Array}
     339                         */
     340                        getAll: function get(ids) {
     341                                var els = [],
     342                                        el;
    309343
    310         /**
    311          * Get array of DOM Elements by their ids.
    312          *
    313          * @method get
    314          * @param {String} id Identifier of the DOM Element
    315          * @return {Array}
    316         */
    317         getAll : function get(ids) {
    318                 var els = [], el;
     344                                if (plupload.typeOf(ids) !== 'array') {
     345                                        ids = [ids];
     346                                }
    319347
    320                 if (plupload.typeOf(ids) !== 'array') {
    321                         ids = [ids];
    322                 }
     348                                var i = ids.length;
     349                                while (i--) {
     350                                        el = plupload.get(ids[i]);
     351                                        if (el) {
     352                                                els.push(el);
     353                                        }
     354                                }
    323355
    324                 var i = ids.length;
    325                 while (i--) {
    326                         el = plupload.get(ids[i]);
    327                         if (el) {
    328                                 els.push(el);
    329                         }
    330                 }
     356                                return els.length ? els : null;
     357                        },
    331358
    332                 return els.length ? els : null;
    333         },
     359                        /**
     360                        Get DOM element by id
    334361
    335         /**
    336         Get DOM element by id
     362                        @method get
     363                        @param {String} id Identifier of the DOM Element
     364                        @return {Node}
     365                        */
     366                        get: u.Dom.get,
    337367
    338         @method get
    339         @param {String} id Identifier of the DOM Element
    340         @return {Node}
    341         */
    342         get: o.get,
     368                        /**
     369                         * Executes the callback function for each item in array/object. If you return false in the
     370                         * callback it will break the loop.
     371                         *
     372                         * @method each
     373                         * @static
     374                         * @param {Object} obj Object to iterate.
     375                         * @param {function} callback Callback function to execute for each item.
     376                         */
     377                        each: u.Basic.each,
    343378
    344         /**
    345          * Executes the callback function for each item in array/object. If you return false in the
    346          * callback it will break the loop.
    347          *
    348          * @method each
    349          * @static
    350          * @param {Object} obj Object to iterate.
    351          * @param {function} callback Callback function to execute for each item.
    352         */
    353         each : o.each,
     379                        /**
     380                         * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
     381                         *
     382                         * @method getPos
     383                         * @static
     384                         * @param {Element} node HTML element or element id to get x, y position from.
     385                         * @param {Element} root Optional root element to stop calculations at.
     386                         * @return {object} Absolute position of the specified element object with x, y fields.
     387                        */
     388                        getPos: u.Dom.getPos,
    354389
    355         /**
    356          * Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
    357          *
    358          * @method getPos
    359          * @static
    360          * @param {Element} node HTML element or element id to get x, y position from.
    361          * @param {Element} root Optional root element to stop calculations at.
    362          * @return {object} Absolute position of the specified element object with x, y fields.
    363          */
    364         getPos : o.getPos,
     390                        /**
     391                         * Returns the size of the specified node in pixels.
     392                         *
     393                         * @method getSize
     394                         * @static
     395                         * @param {Node} node Node to get the size of.
     396                         * @return {Object} Object with a w and h property.
     397                         */
     398                        getSize: u.Dom.getSize,
    365399
    366         /**
    367          * Returns the size of the specified node in pixels.
    368          *
    369          * @method getSize
    370          * @static
    371          * @param {Node} node Node to get the size of.
    372          * @return {Object} Object with a w and h property.
    373          */
    374         getSize : o.getSize,
     400                        /**
     401                         * Encodes the specified string.
     402                         *
     403                         * @method xmlEncode
     404                         * @static
     405                         * @param {String} s String to encode.
     406                         * @return {String} Encoded string.
     407                         */
     408                        xmlEncode: function(str) {
     409                                var xmlEncodeChars = {
     410                                                '<': 'lt',
     411                                                '>': 'gt',
     412                                                '&': 'amp',
     413                                                '"': 'quot',
     414                                                '\'': '#39'
     415                                        },
     416                                        xmlEncodeRegExp = /[<>&\"\']/g;
    375417
    376         /**
    377          * Encodes the specified string.
    378          *
    379          * @method xmlEncode
    380          * @static
    381          * @param {String} s String to encode.
    382          * @return {String} Encoded string.
    383          */
    384         xmlEncode : function(str) {
    385                 var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
     418                                return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
     419                                        return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
     420                                }) : str;
     421                        },
    386422
    387                 return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
    388                         return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
    389                 }) : str;
    390         },
     423                        /**
     424                         * Forces anything into an array.
     425                         *
     426                         * @method toArray
     427                         * @static
     428                         * @param {Object} obj Object with length field.
     429                         * @return {Array} Array object containing all items.
     430                         */
     431                        toArray: u.Basic.toArray,
    391432
    392         /**
    393          * Forces anything into an array.
    394          *
    395          * @method toArray
    396          * @static
    397          * @param {Object} obj Object with length field.
    398          * @return {Array} Array object containing all items.
    399          */
    400         toArray : o.toArray,
     433                        /**
     434                         * Find an element in array and return its index if present, otherwise return -1.
     435                         *
     436                         * @method inArray
     437                         * @static
     438                         * @param {mixed} needle Element to find
     439                         * @param {Array} array
     440                         * @return {Int} Index of the element, or -1 if not found
     441                         */
     442                        inArray: u.Basic.inArray,
    401443
    402         /**
    403          * Find an element in array and return its index if present, otherwise return -1.
    404          *
    405          * @method inArray
    406          * @static
    407          * @param {mixed} needle Element to find
    408          * @param {Array} array
    409          * @return {Int} Index of the element, or -1 if not found
    410          */
    411         inArray : o.inArray,
     444                        /**
     445                        Recieve an array of functions (usually async) to call in sequence, each  function
     446                        receives a callback as first argument that it should call, when it completes. Finally,
     447                        after everything is complete, main callback is called. Passing truthy value to the
     448                        callback as a first argument will interrupt the sequence and invoke main callback
     449                        immediately.
    412450
    413         /**
    414          * Extends the language pack object with new items.
    415          *
    416          * @method addI18n
    417          * @static
    418          * @param {Object} pack Language pack items to add.
    419          * @return {Object} Extended language pack object.
    420          */
    421         addI18n : o.addI18n,
     451                        @method inSeries
     452                        @static
     453                        @param {Array} queue Array of functions to call in sequence
     454                        @param {Function} cb Main callback that is called in the end, or in case of error
     455                        */
     456                        inSeries: u.Basic.inSeries,
    422457
    423         /**
    424          * Translates the specified string by checking for the english string in the language pack lookup.
    425         *
    426          * @method translate
    427         * @static
    428          * @param {String} str String to look for.
    429          * @return {String} Translated string or the input string if it wasn't found.
    430         */
    431         translate : o.translate,
     458                        /**
     459                         * Extends the language pack object with new items.
     460                        *
     461                         * @method addI18n
     462                        * @static
     463                         * @param {Object} pack Language pack items to add.
     464                         * @return {Object} Extended language pack object.
     465                        */
     466                        addI18n: o.core.I18n.addI18n,
    432467
    433         /**
    434          * Checks if object is empty.
    435         *
    436          * @method isEmptyObj
    437         * @static
    438          * @param {Object} obj Object to check.
    439          * @return {Boolean}
    440         */
    441         isEmptyObj : o.isEmptyObj,
     468                        /**
     469                         * Translates the specified string by checking for the english string in the language pack lookup.
     470                        *
     471                         * @method translate
     472                        * @static
     473                         * @param {String} str String to look for.
     474                         * @return {String} Translated string or the input string if it wasn't found.
     475                        */
     476                        translate: o.core.I18n.translate,
    442477
    443         /**
    444          * Checks if specified DOM element has specified class.
    445          *
    446          * @method hasClass
    447          * @static
    448          * @param {Object} obj DOM element like object to add handler to.
    449          * @param {String} name Class name
    450          */
    451         hasClass : o.hasClass,
     478                        /**
     479                         * Pseudo sprintf implementation - simple way to replace tokens with specified values.
     480                         *
     481                         * @param {String} str String with tokens
     482                         * @return {String} String with replaced tokens
     483                         */
     484                        sprintf: u.Basic.sprintf,
    452485
    453         /**
    454          * Adds specified className to specified DOM element.
    455         *
    456          * @method addClass
    457         * @static
    458          * @param {Object} obj DOM element like object to add handler to.
    459          * @param {String} name Class name
    460         */
    461         addClass : o.addClass,
     486                        /**
     487                         * Checks if object is empty.
     488                        *
     489                         * @method isEmptyObj
     490                        * @static
     491                         * @param {Object} obj Object to check.
     492                         * @return {Boolean}
     493                        */
     494                        isEmptyObj: u.Basic.isEmptyObj,
    462495
    463         /**
    464          * Removes specified className from specified DOM element.
    465         *
    466          * @method removeClass
    467         * @static
    468         * @param {Object} obj DOM element like object to add handler to.
    469         * @param {String} name Class name
    470         */
    471         removeClass : o.removeClass,
     496                        /**
     497                         * Checks if specified DOM element has specified class.
     498                        *
     499                         * @method hasClass
     500                        * @static
     501                        * @param {Object} obj DOM element like object to add handler to.
     502                        * @param {String} name Class name
     503                        */
     504                        hasClass: u.Dom.hasClass,
    472505
    473         /**
    474          * Returns a given computed style of a DOM element.
    475         *
    476          * @method getStyle
    477         * @static
    478          * @param {Object} obj DOM element like object.
    479          * @param {String} name Style you want to get from the DOM element
    480         */
    481         getStyle : o.getStyle,
     506                        /**
     507                         * Adds specified className to specified DOM element.
     508                        *
     509                         * @method addClass
     510                        * @static
     511                         * @param {Object} obj DOM element like object to add handler to.
     512                         * @param {String} name Class name
     513                        */
     514                        addClass: u.Dom.addClass,
    482515
    483         /**
    484          * Adds an event handler to the specified object and store reference to the handler
    485          * in objects internal Plupload registry (@see removeEvent).
    486          *
    487          * @method addEvent
    488          * @static
    489          * @param {Object} obj DOM element like object to add handler to.
    490          * @param {String} name Name to add event listener to.
    491          * @param {Function} callback Function to call when event occurs.
    492          * @param {String} (optional) key that might be used to add specifity to the event record.
    493          */
    494         addEvent : o.addEvent,
     516                        /**
     517                         * Removes specified className from specified DOM element.
     518                         *
     519                         * @method removeClass
     520                         * @static
     521                         * @param {Object} obj DOM element like object to add handler to.
     522                         * @param {String} name Class name
     523                         */
     524                        removeClass: u.Dom.removeClass,
    495525
    496         /**
    497          * Remove event handler from the specified object. If third argument (callback)
    498          * is not specified remove all events with the specified name.
    499          *
    500          * @method removeEvent
    501          * @static
    502          * @param {Object} obj DOM element to remove event listener(s) from.
    503          * @param {String} name Name of event listener to remove.
    504          * @param {Function|String} (optional) might be a callback or unique key to match.
    505          */
    506         removeEvent: o.removeEvent,
     526                        /**
     527                         * Returns a given computed style of a DOM element.
     528                         *
     529                         * @method getStyle
     530                         * @static
     531                         * @param {Object} obj DOM element like object.
     532                         * @param {String} name Style you want to get from the DOM element
     533                         */
     534                        getStyle: u.Dom.getStyle,
    507535
    508         /**
    509          * Remove all kind of events from the specified object
    510          *
    511          * @method removeAllEvents
    512          * @static
    513          * @param {Object} obj DOM element to remove event listeners from.
    514          * @param {String} (optional) unique key to match, when removing events.
    515          */
    516         removeAllEvents: o.removeAllEvents,
     536                        /**
     537                         * Adds an event handler to the specified object and store reference to the handler
     538                         * in objects internal Plupload registry (@see removeEvent).
     539                         *
     540                         * @method addEvent
     541                         * @static
     542                         * @param {Object} obj DOM element like object to add handler to.
     543                         * @param {String} name Name to add event listener to.
     544                         * @param {Function} callback Function to call when event occurs.
     545                         * @param {String} (optional) key that might be used to add specifity to the event record.
     546                         */
     547                        addEvent: u.Events.addEvent,
    517548
    518         /**
    519          * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
    520          *
    521          * @method cleanName
    522          * @static
    523          * @param {String} s String to clean up.
    524          * @return {String} Cleaned string.
    525          */
    526         cleanName : function(name) {
    527                 var i, lookup;
     549                        /**
     550                         * Remove event handler from the specified object. If third argument (callback)
     551                         * is not specified remove all events with the specified name.
     552                         *
     553                         * @method removeEvent
     554                         * @static
     555                         * @param {Object} obj DOM element to remove event listener(s) from.
     556                         * @param {String} name Name of event listener to remove.
     557                         * @param {Function|String} (optional) might be a callback or unique key to match.
     558                         */
     559                        removeEvent: u.Events.removeEvent,
    528560
    529                 // Replace diacritics
    530                 lookup = [
    531                         /[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
    532                         /\307/g, 'C', /\347/g, 'c',
    533                         /[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
    534                         /[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
    535                         /\321/g, 'N', /\361/g, 'n',
    536                         /[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
    537                         /[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
    538                 ];
     561                        /**
     562                         * Remove all kind of events from the specified object
     563                         *
     564                         * @method removeAllEvents
     565                         * @static
     566                         * @param {Object} obj DOM element to remove event listeners from.
     567                         * @param {String} (optional) unique key to match, when removing events.
     568                         */
     569                        removeAllEvents: u.Events.removeAllEvents,
    539570
    540                 for (i = 0; i < lookup.length; i += 2) {
    541                         name = name.replace(lookup[i], lookup[i + 1]);
    542                 }
     571                        /**
     572                         * Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
     573                         *
     574                         * @method cleanName
     575                         * @static
     576                         * @param {String} s String to clean up.
     577                         * @return {String} Cleaned string.
     578                         */
     579                        cleanName: function(name) {
     580                                var i, lookup;
    543581
    544                 // Replace whitespace
    545                 name = name.replace(/\s+/g, '_');
     582                                // Replace diacritics
     583                                lookup = [
     584                                        /[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
     585                                        /\307/g, 'C', /\347/g, 'c',
     586                                        /[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
     587                                        /[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
     588                                        /\321/g, 'N', /\361/g, 'n',
     589                                        /[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
     590                                        /[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
     591                                ];
    546592
    547                 // Remove anything else
    548                 name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
     593                                for (i = 0; i < lookup.length; i += 2) {
     594                                        name = name.replace(lookup[i], lookup[i + 1]);
     595                                }
    549596
    550                 return name;
    551         },
     597                                // Replace whitespace
     598                                name = name.replace(/\s+/g, '_');
    552599
    553         /**
    554          * Builds a full url out of a base URL and an object with items to append as query string items.
    555          *
    556          * @method buildUrl
    557          * @static
    558          * @param {String} url Base URL to append query string items to.
    559          * @param {Object} items Name/value object to serialize as a querystring.
    560          * @return {String} String with url + serialized query string items.
    561          */
    562         buildUrl : function(url, items) {
    563                 var query = '';
     600                                // Remove anything else
     601                                name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
    564602
    565                 plupload.each(items, function(value, name) {
    566                         query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
    567                 });
     603                                return name;
     604                        },
    568605
    569                 if (query) {
    570                         url += (url.indexOf('?') > 0 ? '&' : '?') + query;
    571                 }
     606                        /**
     607                         * Builds a full url out of a base URL and an object with items to append as query string items.
     608                         *
     609                         * @method buildUrl
     610                         * @static
     611                         * @param {String} url Base URL to append query string items to.
     612                         * @param {Object} items Name/value object to serialize as a querystring.
     613                         * @return {String} String with url + serialized query string items.
     614                         */
     615                        buildUrl: function(url, items) {
     616                                var query = '';
    572617
    573                 return url;
    574         },
     618                                plupload.each(items, function(value, name) {
     619                                        query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
     620                                });
    575621
    576         /**
    577          * Formats the specified number as a size string for example 1024 becomes 1 KB.
    578          *
    579          * @method formatSize
    580          * @static
    581          * @param {Number} size Size to format as string.
    582          * @return {String} Formatted size string.
    583          */
    584         formatSize : function(size) {
     622                                if (query) {
     623                                        url += (url.indexOf('?') > 0 ? '&' : '?') + query;
     624                                }
    585625
    586                 if (size === undef || /\D/.test(size)) {
    587                         return plupload.translate('N/A');
    588                 }
     626                                return url;
     627                        },
    589628
    590                 function round(num, precision) {
    591                         return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
    592                 }
     629                        /**
     630                         * Formats the specified number as a size string for example 1024 becomes 1 KB.
     631                         *
     632                         * @method formatSize
     633                         * @static
     634                         * @param {Number} size Size to format as string.
     635                         * @return {String} Formatted size string.
     636                         */
     637                        formatSize: function(size) {
    593638
    594                 var boundary = Math.pow(1024, 4);
     639                                if (size === undef || /\D/.test(size)) {
     640                                        return plupload.translate('N/A');
     641                                }
    595642
    596                 // TB
    597                 if (size > boundary) {
    598                         return round(size / boundary, 1) + " " + plupload.translate('tb');
    599                 }
     643                                function round(num, precision) {
     644                                        return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
     645                                }
    600646
    601                 // GB
    602                 if (size > (boundary/=1024)) {
    603                         return round(size / boundary, 1) + " " + plupload.translate('gb');
    604                 }
     647                                var boundary = Math.pow(1024, 4);
    605648
    606                 // MB
    607                 if (size > (boundary/=1024)) {
    608                         return round(size / boundary, 1) + " " + plupload.translate('mb');
    609                 }
     649                                // TB
     650                                if (size > boundary) {
     651                                        return round(size / boundary, 1) + " " + plupload.translate('tb');
     652                                }
    610653
    611                 // KB
    612                 if (size > 1024) {
    613                         return Math.round(size / 1024) + " " + plupload.translate('kb');
    614                 }
     654                                // GB
     655                                if (size > (boundary /= 1024)) {
     656                                        return round(size / boundary, 1) + " " + plupload.translate('gb');
     657                                }
    615658
    616                 return size + " " + plupload.translate('b');
    617         },
     659                                // MB
     660                                if (size > (boundary /= 1024)) {
     661                                        return round(size / boundary, 1) + " " + plupload.translate('mb');
     662                                }
    618663
     664                                // KB
     665                                if (size > 1024) {
     666                                        return Math.round(size / 1024) + " " + plupload.translate('kb');
     667                                }
    619668
    620         /**
    621          * Parses the specified size string into a byte value. For example 10kb becomes 10240.
    622          *
    623          * @method parseSize
    624          * @static
    625          * @param {String|Number} size String to parse or number to just pass through.
    626          * @return {Number} Size in bytes.
    627          */
    628         parseSize : o.parseSizeStr,
     669                                return size + " " + plupload.translate('b');
     670                        },
    629671
    630672
    631         /**
    632          * A way to predict what runtime will be choosen in the current environment with the
    633          * specified settings.
    634          *
    635          * @method predictRuntime
    636          * @static
    637          * @param {Object|String} config Plupload settings to check
    638          * @param {String} [runtimes] Comma-separated list of runtimes to check against
    639          * @return {String} Type of compatible runtime
    640          */
    641         predictRuntime : function(config, runtimes) {
    642                 var up, runtime;
     673                        /**
     674                         * Parses the specified size string into a byte value. For example 10kb becomes 10240.
     675                         *
     676                         * @method parseSize
     677                         * @static
     678                         * @param {String|Number} size String to parse or number to just pass through.
     679                         * @return {Number} Size in bytes.
     680                         */
     681                        parseSize: u.Basic.parseSizeStr,
    643682
    644                 up = new plupload.Uploader(config);
    645                 runtime = o.Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
    646                 up.destroy();
    647                 return runtime;
    648         },
    649683
    650         /**
    651          * Registers a filter that will be executed for each file added to the queue.
    652          * If callback returns false, file will not be added.
    653          *
    654          * Callback receives two arguments: a value for the filter as it was specified in settings.filters
    655          * and a file to be filtered. Callback is executed in the context of uploader instance.
    656          *
    657          * @method addFileFilter
    658          * @static
    659          * @param {String} name Name of the filter by which it can be referenced in settings.filters
    660          * @param {String} cb Callback - the actual routine that every added file must pass
    661          */
    662         addFileFilter: function(name, cb) {
    663                 fileFilters[name] = cb;
    664         }
    665 };
     684                        /**
     685                         * A way to predict what runtime will be choosen in the current environment with the
     686                         * specified settings.
     687                         *
     688                         * @method predictRuntime
     689                         * @static
     690                         * @param {Object|String} config Plupload settings to check
     691                         * @param {String} [runtimes] Comma-separated list of runtimes to check against
     692                         * @return {String} Type of compatible runtime
     693                         */
     694                        predictRuntime: function(config, runtimes) {
     695                                var up, runtime;
    666696
     697                                up = new plupload.Uploader(config);
     698                                runtime = Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
     699                                up.destroy();
     700                                return runtime;
     701                        },
    667702
    668 plupload.addFileFilter('mime_types', function(filters, file, cb) {
    669         if (filters.length && !filters.regexp.test(file.name)) {
    670                 this.trigger('Error', {
    671                         code : plupload.FILE_EXTENSION_ERROR,
    672                         message : plupload.translate('File extension error.'),
    673                         file : file
     703                        /**
     704                         * Registers a filter that will be executed for each file added to the queue.
     705                         * If callback returns false, file will not be added.
     706                         *
     707                         * Callback receives two arguments: a value for the filter as it was specified in settings.filters
     708                         * and a file to be filtered. Callback is executed in the context of uploader instance.
     709                         *
     710                         * @method addFileFilter
     711                         * @static
     712                         * @param {String} name Name of the filter by which it can be referenced in settings.filters
     713                         * @param {String} cb Callback - the actual routine that every added file must pass
     714                         */
     715                        addFileFilter: function(name, cb) {
     716                                fileFilters[name] = cb;
     717                        }
     718                };
     719
     720
     721                plupload.addFileFilter('mime_types', function(filters, file, cb) {
     722                        if (filters.length && !filters.regexp.test(file.name)) {
     723                                this.trigger('Error', {
     724                                        code: plupload.FILE_EXTENSION_ERROR,
     725                                        message: plupload.translate('File extension error.'),
     726                                        file: file
     727                                });
     728                                cb(false);
     729                        } else {
     730                                cb(true);
     731                        }
    674732                });
    675                 cb(false);
    676         } else {
    677                 cb(true);
    678         }
    679 });
    680733
    681734
    682 plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
    683         var undef;
     735                plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
     736                        var undef;
    684737
    685         maxSize = plupload.parseSize(maxSize);
     738                        maxSize = plupload.parseSize(maxSize);
    686739
    687         // Invalid file size
    688         if (file.size !== undef && maxSize && file.size > maxSize) {
    689                 this.trigger('Error', {
    690                         code : plupload.FILE_SIZE_ERROR,
    691                         message : plupload.translate('File size error.'),
    692                         file : file
     740                        // Invalid file size
     741                        if (file.size !== undef && maxSize && file.size > maxSize) {
     742                                this.trigger('Error', {
     743                                        code: plupload.FILE_SIZE_ERROR,
     744                                        message: plupload.translate('File size error.'),
     745                                        file: file
     746                                });
     747                                cb(false);
     748                        } else {
     749                                cb(true);
     750                        }
    693751                });
    694                 cb(false);
    695         } else {
    696                 cb(true);
    697         }
    698 });
    699752
    700753
    701 plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
    702         if (value) {
    703                 var ii = this.files.length;
    704                 while (ii--) {
    705                         // Compare by name and size (size might be 0 or undefined, but still equivalent for both)
    706                         if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
     754                plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
     755                        if (value) {
     756                                var ii = this.files.length;
     757                                while (ii--) {
     758                                        // Compare by name and size (size might be 0 or undefined, but still equivalent for both)
     759                                        if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
     760                                                this.trigger('Error', {
     761                                                        code: plupload.FILE_DUPLICATE_ERROR,
     762                                                        message: plupload.translate('Duplicate file error.'),
     763                                                        file: file
     764                                                });
     765                                                cb(false);
     766                                                return;
     767                                        }
     768                                }
     769                        }
     770                        cb(true);
     771                });
     772
     773                plupload.addFileFilter('prevent_empty', function(value, file, cb) {
     774                        if (value && !file.size && file.size !== undef) {
    707775                                this.trigger('Error', {
    708                                         code : plupload.FILE_DUPLICATE_ERROR,
    709                                         message : plupload.translate('Duplicate file error.'),
    710                                         file : file
     776                                        code: plupload.FILE_SIZE_ERROR,
     777                                        message: plupload.translate('File size error.'),
     778                                        file: file
    711779                                });
    712780                                cb(false);
    713                                 return;
     781                        } else {
     782                                cb(true);
    714783                        }
    715                 }
    716         }
    717         cb(true);
    718 });
     784                });
    719785
    720786
    721 /**
    722 @class Uploader
    723 @constructor
     787                /**
     788                @class Uploader
     789                @constructor
    724790
    725 @param {Object} settings For detailed information about each option check documentation.
    726         @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
    727         @param {String} settings.url URL of the server-side upload handler.
    728         @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.
    729         @param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
    730         @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.
    731         @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.
    732         @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
    733         @param {Object} [settings.filters={}] Set of file type filters.
    734                 @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`
    735                 @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`.
    736                 @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
    737         @param {String} [settings.flash_swf_url] URL of the Flash swf. (Not used in WordPress)
    738         @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
    739         @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
    740         @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
    741         @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
    742         @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
    743         @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
    744         @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}`
    745                 @param {Number} [settings.resize.width] If image is bigger, it will be resized.
    746                 @param {Number} [settings.resize.height] If image is bigger, it will be resized.
    747                 @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
    748                 @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
    749         @param {String} [settings.runtimes="html5,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
    750         @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap. (Not used in WordPress)
    751         @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
    752         @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).
    753 */
    754 plupload.Uploader = function(options) {
    755         /**
    756         Fires when the current RunTime has been initialized.
    757        
    758         @event Init
    759         @param {plupload.Uploader} uploader Uploader instance sending the event.
    760          */
     791                @param {Object} settings For detailed information about each option check documentation.
     792                        @param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
     793                        @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.
     794                        @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.
     795                        @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.
     796                        @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
     797                        @param {Object} [settings.filters={}] Set of file type filters.
     798                                @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`.
     799                                @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`
     800                                @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
     801                                @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`.
     802                        @param {String} [settings.flash_swf_url] URL of the Flash swf.
     803                        @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
     804                        @param {String} [settings.http_method="POST"] HTTP method to use during upload (only PUT or POST allowed).
     805                        @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
     806                        @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
     807                        @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
     808                        @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
     809                        @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
     810                        @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}`
     811                                @param {Number} [settings.resize.width] If image is bigger, it will be resized.
     812                                @param {Number} [settings.resize.height] If image is bigger, it will be resized.
     813                                @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
     814                                @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
     815                        @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.
     816                        @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
     817                        @param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
     818                        @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).
     819                        @param {String} settings.url URL of the server-side upload handler.
     820                        @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
    761821
    762         /**
    763         Fires after the init event incase you need to perform actions there.
    764        
    765         @event PostInit
    766         @param {plupload.Uploader} uploader Uploader instance sending the event.
    767          */
     822                */
     823                plupload.Uploader = function(options) {
     824                        /**
     825                        Fires when the current RunTime has been initialized.
    768826
    769         /**
    770         Fires when the option is changed in via uploader.setOption().
    771        
    772         @event OptionChanged
    773         @since 2.1
    774         @param {plupload.Uploader} uploader Uploader instance sending the event.
    775         @param {String} name Name of the option that was changed
    776         @param {Mixed} value New value for the specified option
    777         @param {Mixed} oldValue Previous value of the option
    778          */
     827                        @event Init
     828                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     829                         */
    779830
    780         /**
    781         Fires when the silverlight/flash or other shim needs to move.
    782        
    783         @event Refresh
    784         @param {plupload.Uploader} uploader Uploader instance sending the event.
    785          */
     831                        /**
     832                        Fires after the init event incase you need to perform actions there.
    786833
    787         /**
    788         Fires when the overall state is being changed for the upload queue.
    789        
    790         @event StateChanged
    791         @param {plupload.Uploader} uploader Uploader instance sending the event.
    792          */
     834                        @event PostInit
     835                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     836                         */
    793837
    794         /**
    795         Fires when browse_button is clicked and browse dialog shows.
    796        
    797         @event Browse
    798         @since 2.1.2
    799         @param {plupload.Uploader} uploader Uploader instance sending the event.
    800          */     
     838                        /**
     839                        Fires when the option is changed in via uploader.setOption().
    801840
    802         /**
    803         Fires for every filtered file before it is added to the queue.
    804        
    805         @event FileFiltered
    806         @since 2.1
    807         @param {plupload.Uploader} uploader Uploader instance sending the event.
    808         @param {plupload.File} file Another file that has to be added to the queue.
    809          */
     841                        @event OptionChanged
     842                        @since 2.1
     843                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     844                        @param {String} name Name of the option that was changed
     845                        @param {Mixed} value New value for the specified option
     846                        @param {Mixed} oldValue Previous value of the option
     847                         */
    810848
    811         /**
    812         Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
    813        
    814         @event QueueChanged
    815         @param {plupload.Uploader} uploader Uploader instance sending the event.
    816          */
     849                        /**
     850                        Fires when the silverlight/flash or other shim needs to move.
    817851
    818         /**
    819         Fires after files were filtered and added to the queue.
    820        
    821         @event FilesAdded
    822         @param {plupload.Uploader} uploader Uploader instance sending the event.
    823         @param {Array} files Array of file objects that were added to queue by the user.
    824          */
     852                        @event Refresh
     853                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     854                         */
    825855
    826         /**
    827         Fires when file is removed from the queue.
    828        
    829         @event FilesRemoved
    830         @param {plupload.Uploader} uploader Uploader instance sending the event.
    831         @param {Array} files Array of files that got removed.
    832          */
     856                        /**
     857                        Fires when the overall state is being changed for the upload queue.
    833858
    834         /**
    835         Fires just before a file is uploaded. Can be used to cancel the upload for the specified file
    836         by returning false from the handler.
    837        
    838         @event BeforeUpload
    839         @param {plupload.Uploader} uploader Uploader instance sending the event.
    840         @param {plupload.File} file File to be uploaded.
    841          */
     859                        @event StateChanged
     860                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     861                         */
    842862
    843         /**
    844         Fires when a file is to be uploaded by the runtime.
    845        
    846         @event UploadFile
    847         @param {plupload.Uploader} uploader Uploader instance sending the event.
    848         @param {plupload.File} file File to be uploaded.
    849          */
     863                        /**
     864                        Fires when browse_button is clicked and browse dialog shows.
    850865
    851         /**
    852         Fires while a file is being uploaded. Use this event to update the current file upload progress.
    853        
    854         @event UploadProgress
    855         @param {plupload.Uploader} uploader Uploader instance sending the event.
    856         @param {plupload.File} file File that is currently being uploaded.
    857          */     
     866                        @event Browse
     867                        @since 2.1.2
     868                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     869                         */
    858870
    859         /**
    860         Fires when file chunk is uploaded.
    861        
    862         @event ChunkUploaded
    863         @param {plupload.Uploader} uploader Uploader instance sending the event.
    864         @param {plupload.File} file File that the chunk was uploaded for.
    865         @param {Object} result Object with response properties.
    866                 @param {Number} result.offset The amount of bytes the server has received so far, including this chunk.
    867                 @param {Number} result.total The size of the file.
    868                 @param {String} result.response The response body sent by the server.
    869                 @param {Number} result.status The HTTP status code sent by the server.
    870                 @param {String} result.responseHeaders All the response headers as a single string.
    871          */
     871                        /**
     872                        Fires for every filtered file before it is added to the queue.
    872873
    873         /**
    874         Fires when a file is successfully uploaded.
    875        
    876         @event FileUploaded
    877         @param {plupload.Uploader} uploader Uploader instance sending the event.
    878         @param {plupload.File} file File that was uploaded.
    879         @param {Object} result Object with response properties.
    880                 @param {String} result.response The response body sent by the server.
    881                 @param {Number} result.status The HTTP status code sent by the server.
    882                 @param {String} result.responseHeaders All the response headers as a single string.
    883          */
     874                        @event FileFiltered
     875                        @since 2.1
     876                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     877                        @param {plupload.File} file Another file that has to be added to the queue.
     878                         */
    884879
    885         /**
    886         Fires when all files in a queue are uploaded.
    887        
    888         @event UploadComplete
    889         @param {plupload.Uploader} uploader Uploader instance sending the event.
    890         @param {Array} files Array of file objects that was added to queue/selected by the user.
    891          */
     880                        /**
     881                        Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
    892882
    893         /**
    894         Fires when a error occurs.
    895        
    896         @event Error
    897         @param {plupload.Uploader} uploader Uploader instance sending the event.
    898         @param {Object} error Contains code, message and sometimes file and other details.
    899                 @param {Number} error.code The plupload error code.
    900                 @param {String} error.message Description of the error (uses i18n).
    901          */
     883                        @event QueueChanged
     884                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     885                         */
    902886
    903         /**
    904         Fires when destroy method is called.
    905        
    906         @event Destroy
    907         @param {plupload.Uploader} uploader Uploader instance sending the event.
    908          */
    909         var uid = plupload.guid()
    910         , settings
    911         , files = []
    912         , preferred_caps = {}
    913         , fileInputs = []
    914         , fileDrops = []
    915         , startTime
    916         , total
    917         , disabled = false
    918         , xhr
    919         ;
     887                        /**
     888                        Fires after files were filtered and added to the queue.
    920889
     890                        @event FilesAdded
     891                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     892                        @param {Array} files Array of file objects that were added to queue by the user.
     893                         */
    921894
    922         // Private methods
    923         function uploadNext() {
    924                 var file, count = 0, i;
     895                        /**
     896                        Fires when file is removed from the queue.
    925897
    926                 if (this.state == plupload.STARTED) {
    927                         // Find first QUEUED file
    928                         for (i = 0; i < files.length; i++) {
    929                                 if (!file && files[i].status == plupload.QUEUED) {
    930                                         file = files[i];
    931                                         if (this.trigger("BeforeUpload", file)) {
    932                                                 file.status = plupload.UPLOADING;
    933                                                 this.trigger("UploadFile", file);
     898                        @event FilesRemoved
     899                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     900                        @param {Array} files Array of files that got removed.
     901                         */
     902
     903                        /**
     904                        Fires just before a file is uploaded. Can be used to cancel the upload for the specified file
     905                        by returning false from the handler.
     906
     907                        @event BeforeUpload
     908                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     909                        @param {plupload.File} file File to be uploaded.
     910                         */
     911
     912                        /**
     913                        Fires when a file is to be uploaded by the runtime.
     914
     915                        @event UploadFile
     916                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     917                        @param {plupload.File} file File to be uploaded.
     918                         */
     919
     920                        /**
     921                        Fires while a file is being uploaded. Use this event to update the current file upload progress.
     922
     923                        @event UploadProgress
     924                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     925                        @param {plupload.File} file File that is currently being uploaded.
     926                         */
     927
     928                        /**
     929                         * Fires just before a chunk is uploaded. This event enables you to override settings
     930                         * on the uploader instance before the chunk is uploaded.
     931                         *
     932                         * @event BeforeChunkUpload
     933                         * @param {plupload.Uploader} uploader Uploader instance sending the event.
     934                         * @param {plupload.File} file File to be uploaded.
     935                         * @param {Object} args POST params to be sent.
     936                         * @param {Blob} chunkBlob Current blob.
     937                         * @param {offset} offset Current offset.
     938                         */
     939
     940                        /**
     941                        Fires when file chunk is uploaded.
     942
     943                        @event ChunkUploaded
     944                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     945                        @param {plupload.File} file File that the chunk was uploaded for.
     946                        @param {Object} result Object with response properties.
     947                                @param {Number} result.offset The amount of bytes the server has received so far, including this chunk.
     948                                @param {Number} result.total The size of the file.
     949                                @param {String} result.response The response body sent by the server.
     950                                @param {Number} result.status The HTTP status code sent by the server.
     951                                @param {String} result.responseHeaders All the response headers as a single string.
     952                         */
     953
     954                        /**
     955                        Fires when a file is successfully uploaded.
     956
     957                        @event FileUploaded
     958                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     959                        @param {plupload.File} file File that was uploaded.
     960                        @param {Object} result Object with response properties.
     961                                @param {String} result.response The response body sent by the server.
     962                                @param {Number} result.status The HTTP status code sent by the server.
     963                                @param {String} result.responseHeaders All the response headers as a single string.
     964                         */
     965
     966                        /**
     967                        Fires when all files in a queue are uploaded.
     968
     969                        @event UploadComplete
     970                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     971                        @param {Array} files Array of file objects that was added to queue/selected by the user.
     972                         */
     973
     974                        /**
     975                        Fires when a error occurs.
     976
     977                        @event Error
     978                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     979                        @param {Object} error Contains code, message and sometimes file and other details.
     980                                @param {Number} error.code The plupload error code.
     981                                @param {String} error.message Description of the error (uses i18n).
     982                         */
     983
     984                        /**
     985                        Fires when destroy method is called.
     986
     987                        @event Destroy
     988                        @param {plupload.Uploader} uploader Uploader instance sending the event.
     989                         */
     990                        var uid = plupload.guid(),
     991                                settings, files = [],
     992                                preferred_caps = {},
     993                                fileInputs = [],
     994                                fileDrops = [],
     995                                startTime, total, disabled = false,
     996                                xhr;
     997
     998
     999                        // Private methods
     1000                        function uploadNext() {
     1001                                var file, count = 0,
     1002                                        i;
     1003
     1004                                if (this.state == plupload.STARTED) {
     1005                                        // Find first QUEUED file
     1006                                        for (i = 0; i < files.length; i++) {
     1007                                                if (!file && files[i].status == plupload.QUEUED) {
     1008                                                        file = files[i];
     1009                                                        if (this.trigger("BeforeUpload", file)) {
     1010                                                                file.status = plupload.UPLOADING;
     1011                                                                this.trigger("UploadFile", file);
     1012                                                        }
     1013                                                } else {
     1014                                                        count++;
     1015                                                }
    9341016                                        }
    935                                 } else {
    936                                         count++;
     1017
     1018                                        // All files are DONE or FAILED
     1019                                        if (count == files.length) {
     1020                                                if (this.state !== plupload.STOPPED) {
     1021                                                        this.state = plupload.STOPPED;
     1022                                                        this.trigger("StateChanged");
     1023                                                }
     1024                                                this.trigger("UploadComplete", files);
     1025                                        }
    9371026                                }
    9381027                        }
    9391028
    940                         // All files are DONE or FAILED
    941                         if (count == files.length) {
    942                                 if (this.state !== plupload.STOPPED) {
    943                                         this.state = plupload.STOPPED;
    944                                         this.trigger("StateChanged");
    945                                 }
    946                                 this.trigger("UploadComplete", files);
     1029
     1030                        function calcFile(file) {
     1031                                file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
     1032                                calc();
    9471033                        }
    948                 }
    949         }
    9501034
    9511035
    952         function calcFile(file) {
    953                 file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
    954                 calc();
    955         }
     1036                        function calc() {
     1037                                var i, file;
     1038                                var loaded;
     1039                                var loadedDuringCurrentSession = 0;
    9561040
     1041                                // Reset stats
     1042                                total.reset();
    9571043
    958         function calc() {
    959                 var i, file;
     1044                                // Check status, size, loaded etc on all files
     1045                                for (i = 0; i < files.length; i++) {
     1046                                        file = files[i];
    9601047
    961                 // Reset stats
    962                 total.reset();
     1048                                        if (file.size !== undef) {
     1049                                                // We calculate totals based on original file size
     1050                                                total.size += file.origSize;
    9631051
    964                 // Check status, size, loaded etc on all files
    965                 for (i = 0; i < files.length; i++) {
    966                         file = files[i];
     1052                                                // Since we cannot predict file size after resize, we do opposite and
     1053                                                // interpolate loaded amount to match magnitude of total
     1054                                                loaded = file.loaded * file.origSize / file.size;
    9671055
    968                         if (file.size !== undef) {
    969                                 // We calculate totals based on original file size
    970                                 total.size += file.origSize;
     1056                                                if (!file.completeTimestamp || file.completeTimestamp > startTime) {
     1057                                                        loadedDuringCurrentSession += loaded;
     1058                                                }
    9711059
    972                                 // Since we cannot predict file size after resize, we do opposite and
    973                                 // interpolate loaded amount to match magnitude of total
    974                                 total.loaded += file.loaded * file.origSize / file.size;
    975                         } else {
    976                                 total.size = undef;
     1060                                                total.loaded += loaded;
     1061                                        } else {
     1062                                                total.size = undef;
     1063                                        }
     1064
     1065                                        if (file.status == plupload.DONE) {
     1066                                                total.uploaded++;
     1067                                        } else if (file.status == plupload.FAILED) {
     1068                                                total.failed++;
     1069                                        } else {
     1070                                                total.queued++;
     1071                                        }
     1072                                }
     1073
     1074                                // If we couldn't calculate a total file size then use the number of files to calc percent
     1075                                if (total.size === undef) {
     1076                                        total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
     1077                                } else {
     1078                                        total.bytesPerSec = Math.ceil(loadedDuringCurrentSession / ((+new Date() - startTime || 1) / 1000.0));
     1079                                        total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
     1080                                }
    9771081                        }
    9781082
    979                         if (file.status == plupload.DONE) {
    980                                 total.uploaded++;
    981                         } else if (file.status == plupload.FAILED) {
    982                                 total.failed++;
    983                         } else {
    984                                 total.queued++;
     1083
     1084                        function getRUID() {
     1085                                var ctrl = fileInputs[0] || fileDrops[0];
     1086                                if (ctrl) {
     1087                                        return ctrl.getRuntime().uid;
     1088                                }
     1089                                return false;
    9851090                        }
    986                 }
    9871091
    988                 // If we couldn't calculate a total file size then use the number of files to calc percent
    989                 if (total.size === undef) {
    990                         total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
    991                 } else {
    992                         total.bytesPerSec = Math.ceil(total.loaded / ((+new Date() - startTime || 1) / 1000.0));
    993                         total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
    994                 }
    995         }
    9961092
     1093                        function bindEventListeners() {
     1094                                this.bind('FilesAdded FilesRemoved', function(up) {
     1095                                        up.trigger('QueueChanged');
     1096                                        up.refresh();
     1097                                });
    9971098
    998         function getRUID() {
    999                 var ctrl = fileInputs[0] || fileDrops[0];
    1000                 if (ctrl) {
    1001                         return ctrl.getRuntime().uid;
    1002                 }
    1003                 return false;
    1004         }
     1099                                this.bind('CancelUpload', onCancelUpload);
    10051100
     1101                                this.bind('BeforeUpload', onBeforeUpload);
    10061102
    1007         function runtimeCan(file, cap) {
    1008                 if (file.ruid) {
    1009                         var info = o.Runtime.getInfo(file.ruid);
    1010                         if (info) {
    1011                                 return info.can(cap);
    1012                         }
    1013                 }
    1014                 return false;
    1015         }
     1103                                this.bind('UploadFile', onUploadFile);
    10161104
     1105                                this.bind('UploadProgress', onUploadProgress);
    10171106
    1018         function bindEventListeners() {
    1019                 this.bind('FilesAdded FilesRemoved', function(up) {
    1020                         up.trigger('QueueChanged');
    1021                         up.refresh();
    1022                 });
     1107                                this.bind('StateChanged', onStateChanged);
    10231108
    1024                 this.bind('CancelUpload', onCancelUpload);
    1025                
    1026                 this.bind('BeforeUpload', onBeforeUpload);
     1109                                this.bind('QueueChanged', calc);
    10271110
    1028                 this.bind('UploadFile', onUploadFile);
     1111                                this.bind('Error', onError);
    10291112
    1030                 this.bind('UploadProgress', onUploadProgress);
     1113                                this.bind('FileUploaded', onFileUploaded);
    10311114
    1032                 this.bind('StateChanged', onStateChanged);
     1115                                this.bind('Destroy', onDestroy);
     1116                        }
    10331117
    1034                 this.bind('QueueChanged', calc);
    10351118
    1036                 this.bind('Error', onError);
     1119                        function initControls(settings, cb) {
     1120                                var self = this,
     1121                                        inited = 0,
     1122                                        queue = [];
    10371123
    1038                 this.bind('FileUploaded', onFileUploaded);
     1124                                // common settings
     1125                                var options = {
     1126                                        runtime_order: settings.runtimes,
     1127                                        required_caps: settings.required_features,
     1128                                        preferred_caps: preferred_caps,
     1129                                        swf_url: settings.flash_swf_url,
     1130                                        xap_url: settings.silverlight_xap_url
     1131                                };
    10391132
    1040                 this.bind('Destroy', onDestroy);
    1041         }
     1133                                // add runtime specific options if any
     1134                                plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
     1135                                        if (settings[runtime]) {
     1136                                                options[runtime] = settings[runtime];
     1137                                        }
     1138                                });
    10421139
     1140                                // initialize file pickers - there can be many
     1141                                if (settings.browse_button) {
     1142                                        plupload.each(settings.browse_button, function(el) {
     1143                                                queue.push(function(cb) {
     1144                                                        var fileInput = new o.file.FileInput(plupload.extend({}, options, {
     1145                                                                accept: settings.filters.mime_types,
     1146                                                                name: settings.file_data_name,
     1147                                                                multiple: settings.multi_selection,
     1148                                                                container: settings.container,
     1149                                                                browse_button: el
     1150                                                        }));
    10431151
    1044         function initControls(settings, cb) {
    1045                 var self = this, inited = 0, queue = [];
     1152                                                        fileInput.onready = function() {
     1153                                                                var info = Runtime.getInfo(this.ruid);
    10461154
    1047                 // common settings
    1048                 var options = {
    1049                         runtime_order: settings.runtimes,
    1050                         required_caps: settings.required_features,
    1051                         preferred_caps: preferred_caps
    1052                 };
     1155                                                                // for backward compatibility
     1156                                                                plupload.extend(self.features, {
     1157                                                                        chunks: info.can('slice_blob'),
     1158                                                                        multipart: info.can('send_multipart'),
     1159                                                                        multi_selection: info.can('select_multiple')
     1160                                                                });
    10531161
    1054                 // add runtime specific options if any
    1055                 plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
    1056                         if (settings[runtime]) {
    1057                                 options[runtime] = settings[runtime];
    1058                         }
    1059                 });
     1162                                                                inited++;
     1163                                                                fileInputs.push(this);
     1164                                                                cb();
     1165                                                        };
    10601166
    1061                 // initialize file pickers - there can be many
    1062                 if (settings.browse_button) {
    1063                         plupload.each(settings.browse_button, function(el) {
    1064                                 queue.push(function(cb) {
    1065                                         var fileInput = new o.FileInput(plupload.extend({}, options, {
    1066                                                 accept: settings.filters.mime_types,
    1067                                                 name: settings.file_data_name,
    1068                                                 multiple: settings.multi_selection,
    1069                                                 container: settings.container,
    1070                                                 browse_button: el
    1071                                         }));
     1167                                                        fileInput.onchange = function() {
     1168                                                                self.addFile(this.files);
     1169                                                        };
    10721170
    1073                                         fileInput.onready = function() {
    1074                                                 var info = o.Runtime.getInfo(this.ruid);
     1171                                                        fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
     1172                                                                if (!disabled) {
     1173                                                                        if (settings.browse_button_hover) {
     1174                                                                                if ('mouseenter' === e.type) {
     1175                                                                                        plupload.addClass(el, settings.browse_button_hover);
     1176                                                                                } else if ('mouseleave' === e.type) {
     1177                                                                                        plupload.removeClass(el, settings.browse_button_hover);
     1178                                                                                }
     1179                                                                        }
    10751180
    1076                                                 // for backward compatibility
    1077                                                 o.extend(self.features, {
    1078                                                         chunks: info.can('slice_blob'),
    1079                                                         multipart: info.can('send_multipart'),
    1080                                                         multi_selection: info.can('select_multiple')
     1181                                                                        if (settings.browse_button_active) {
     1182                                                                                if ('mousedown' === e.type) {
     1183                                                                                        plupload.addClass(el, settings.browse_button_active);
     1184                                                                                } else if ('mouseup' === e.type) {
     1185                                                                                        plupload.removeClass(el, settings.browse_button_active);
     1186                                                                                }
     1187                                                                        }
     1188                                                                }
     1189                                                        });
     1190
     1191                                                        fileInput.bind('mousedown', function() {
     1192                                                                self.trigger('Browse');
     1193                                                        });
     1194
     1195                                                        fileInput.bind('error runtimeerror', function() {
     1196                                                                fileInput = null;
     1197                                                                cb();
     1198                                                        });
     1199
     1200                                                        fileInput.init();
    10811201                                                });
     1202                                        });
     1203                                }
    10821204
    1083                                                 inited++;
    1084                                                 fileInputs.push(this);
    1085                                                 cb();
    1086                                         };
     1205                                // initialize drop zones
     1206                                if (settings.drop_element) {
     1207                                        plupload.each(settings.drop_element, function(el) {
     1208                                                queue.push(function(cb) {
     1209                                                        var fileDrop = new o.file.FileDrop(plupload.extend({}, options, {
     1210                                                                drop_zone: el
     1211                                                        }));
    10871212
    1088                                         fileInput.onchange = function() {
    1089                                                 self.addFile(this.files);
    1090                                         };
     1213                                                        fileDrop.onready = function() {
     1214                                                                var info = Runtime.getInfo(this.ruid);
    10911215
    1092                                         fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
    1093                                                 if (!disabled) {
    1094                                                         if (settings.browse_button_hover) {
    1095                                                                 if ('mouseenter' === e.type) {
    1096                                                                         o.addClass(el, settings.browse_button_hover);
    1097                                                                 } else if ('mouseleave' === e.type) {
    1098                                                                         o.removeClass(el, settings.browse_button_hover);
    1099                                                                 }
    1100                                                         }
     1216                                                                // for backward compatibility
     1217                                                                plupload.extend(self.features, {
     1218                                                                        chunks: info.can('slice_blob'),
     1219                                                                        multipart: info.can('send_multipart'),
     1220                                                                        dragdrop: info.can('drag_and_drop')
     1221                                                                });
    11011222
    1102                                                         if (settings.browse_button_active) {
    1103                                                                 if ('mousedown' === e.type) {
    1104                                                                         o.addClass(el, settings.browse_button_active);
    1105                                                                 } else if ('mouseup' === e.type) {
    1106                                                                         o.removeClass(el, settings.browse_button_active);
    1107                                                                 }
    1108                                                         }
    1109                                                 }
    1110                                         });
     1223                                                                inited++;
     1224                                                                fileDrops.push(this);
     1225                                                                cb();
     1226                                                        };
    11111227
    1112                                         fileInput.bind('mousedown', function() {
    1113                                                 self.trigger('Browse');
    1114                                         });
     1228                                                        fileDrop.ondrop = function() {
     1229                                                                self.addFile(this.files);
     1230                                                        };
    11151231
    1116                                         fileInput.bind('error runtimeerror', function() {
    1117                                                 fileInput = null;
    1118                                                 cb();
     1232                                                        fileDrop.bind('error runtimeerror', function() {
     1233                                                                fileDrop = null;
     1234                                                                cb();
     1235                                                        });
     1236
     1237                                                        fileDrop.init();
     1238                                                });
    11191239                                        });
     1240                                }
    11201241
    1121                                         fileInput.init();
     1242
     1243                                plupload.inSeries(queue, function() {
     1244                                        if (typeof(cb) === 'function') {
     1245                                                cb(inited);
     1246                                        }
    11221247                                });
    1123                         });
    1124                 }
     1248                        }
    11251249
    1126                 // initialize drop zones
    1127                 if (settings.drop_element) {
    1128                         plupload.each(settings.drop_element, function(el) {
    1129                                 queue.push(function(cb) {
    1130                                         var fileDrop = new o.FileDrop(plupload.extend({}, options, {
    1131                                                 drop_zone: el
    1132                                         }));
    11331250
    1134                                         fileDrop.onready = function() {
    1135                                                 var info = o.Runtime.getInfo(this.ruid);
     1251                        function resizeImage(blob, params, runtimeOptions, cb) {
     1252                                var img = new o.image.Image();
    11361253
    1137                                                 // for backward compatibility
    1138                                                 o.extend(self.features, {
    1139                                                         chunks: info.can('slice_blob'),
    1140                                                         multipart: info.can('send_multipart'),
    1141                                                         dragdrop: info.can('drag_and_drop')
    1142                                                 });
    1143 
    1144                                                 inited++;
    1145                                                 fileDrops.push(this);
    1146                                                 cb();
     1254                                try {
     1255                                        img.onload = function() {
     1256                                                // no manipulation required if...
     1257                                                if (params.width > this.width &&
     1258                                                        params.height > this.height &&
     1259                                                        params.quality === undef &&
     1260                                                        params.preserve_headers &&
     1261                                                        !params.crop
     1262                                                ) {
     1263                                                        this.destroy();
     1264                                                        cb(blob);
     1265                                                } else {
     1266                                                        // otherwise downsize
     1267                                                        img.downsize(params.width, params.height, params.crop, params.preserve_headers);
     1268                                                }
    11471269                                        };
    11481270
    1149                                         fileDrop.ondrop = function() {
    1150                                                 self.addFile(this.files);
     1271                                        img.onresize = function() {
     1272                                                var resizedBlob = this.getAsBlob(blob.type, params.quality);
     1273                                                this.destroy();
     1274                                                cb(resizedBlob);
    11511275                                        };
    11521276
    1153                                         fileDrop.bind('error runtimeerror', function() {
    1154                                                 fileDrop = null;
    1155                                                 cb();
     1277                                        img.bind('error runtimeerror', function() {
     1278                                                this.destroy();
     1279                                                cb(blob);
    11561280                                        });
    11571281
    1158                                         fileDrop.init();
    1159                                 });
    1160                         });
    1161                 }
     1282                                        img.load(blob, runtimeOptions);
     1283                                } catch (ex) {
     1284                                        cb(blob);
     1285                                }
     1286                        }
    11621287
    11631288
    1164                 o.inSeries(queue, function() {
    1165                         if (typeof(cb) === 'function') {
    1166                                 cb(inited);
    1167                         }
    1168                 });
    1169         }
     1289                        function setOption(option, value, init) {
     1290                                var self = this,
     1291                                        reinitRequired = false;
    11701292
     1293                                function _setOption(option, value, init) {
     1294                                        var oldValue = settings[option];
    11711295
    1172         function resizeImage(blob, params, cb) {
    1173                 var img = new o.Image();
     1296                                        switch (option) {
     1297                                                case 'max_file_size':
     1298                                                        if (option === 'max_file_size') {
     1299                                                                settings.max_file_size = settings.filters.max_file_size = value;
     1300                                                        }
     1301                                                        break;
    11741302
    1175                 try {
    1176                         img.onload = function() {
    1177                                 // no manipulation required if...
    1178                                 if (params.width > this.width &&
    1179                                         params.height > this.height &&
    1180                                         params.quality === undef &&
    1181                                         params.preserve_headers &&
    1182                                         !params.crop
    1183                                 ) {
    1184                                         this.destroy();
    1185                                         return cb(blob);
    1186                                 }
    1187                                 // otherwise downsize
    1188                                 img.downsize(params.width, params.height, params.crop, params.preserve_headers);
    1189                         };
     1303                                                case 'chunk_size':
     1304                                                        if (value = plupload.parseSize(value)) {
     1305                                                                settings[option] = value;
     1306                                                                settings.send_file_name = true;
     1307                                                        }
     1308                                                        break;
    11901309
    1191                         img.onresize = function() {
    1192                                 cb(this.getAsBlob(blob.type, params.quality));
    1193                                 this.destroy();
    1194                         };
     1310                                                case 'multipart':
     1311                                                        settings[option] = value;
     1312                                                        if (!value) {
     1313                                                                settings.send_file_name = true;
     1314                                                        }
     1315                                                        break;
    11951316
    1196                         img.onerror = function() {
    1197                                 cb(blob);
    1198                         };
     1317                                                case 'http_method':
     1318                                                        settings[option] = value.toUpperCase() === 'PUT' ? 'PUT' : 'POST';
     1319                                                        break;
    11991320
    1200                         img.load(blob);
    1201                 } catch(ex) {
    1202                         cb(blob);
    1203                 }
    1204         }
     1321                                                case 'unique_names':
     1322                                                        settings[option] = value;
     1323                                                        if (value) {
     1324                                                                settings.send_file_name = true;
     1325                                                        }
     1326                                                        break;
    12051327
     1328                                                case 'filters':
     1329                                                        // for sake of backward compatibility
     1330                                                        if (plupload.typeOf(value) === 'array') {
     1331                                                                value = {
     1332                                                                        mime_types: value
     1333                                                                };
     1334                                                        }
    12061335
    1207         function setOption(option, value, init) {
    1208                 var self = this, reinitRequired = false;
     1336                                                        if (init) {
     1337                                                                plupload.extend(settings.filters, value);
     1338                                                        } else {
     1339                                                                settings.filters = value;
     1340                                                        }
    12091341
    1210                 function _setOption(option, value, init) {
    1211                         var oldValue = settings[option];
     1342                                                        // if file format filters are being updated, regenerate the matching expressions
     1343                                                        if (value.mime_types) {
     1344                                                                if (plupload.typeOf(value.mime_types) === 'string') {
     1345                                                                        value.mime_types = o.core.utils.Mime.mimes2extList(value.mime_types);
     1346                                                                }
    12121347
    1213                         switch (option) {
    1214                                 case 'max_file_size':
    1215                                         if (option === 'max_file_size') {
    1216                                                 settings.max_file_size = settings.filters.max_file_size = value;
    1217                                         }
    1218                                         break;
     1348                                                                value.mime_types.regexp = (function(filters) {
     1349                                                                        var extensionsRegExp = [];
    12191350
    1220                                 case 'chunk_size':
    1221                                         if (value = plupload.parseSize(value)) {
    1222                                                 settings[option] = value;
    1223                                                 settings.send_file_name = true;
    1224                                         }
    1225                                         break;
     1351                                                                        plupload.each(filters, function(filter) {
     1352                                                                                plupload.each(filter.extensions.split(/,/), function(ext) {
     1353                                                                                        if (/^\s*\*\s*$/.test(ext)) {
     1354                                                                                                extensionsRegExp.push('\\.*');
     1355                                                                                        } else {
     1356                                                                                                extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
     1357                                                                                        }
     1358                                                                                });
     1359                                                                        });
    12261360
    1227                                 case 'multipart':
    1228                                         settings[option] = value;
    1229                                         if (!value) {
    1230                                                 settings.send_file_name = true;
    1231                                         }
    1232                                         break;
     1361                                                                        return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
     1362                                                                }(value.mime_types));
    12331363
    1234                                 case 'unique_names':
    1235                                         settings[option] = value;
    1236                                         if (value) {
    1237                                                 settings.send_file_name = true;
    1238                                         }
    1239                                         break;
     1364                                                                settings.filters.mime_types = value.mime_types;
     1365                                                        }
     1366                                                        break;
    12401367
    1241                                 case 'filters':
    1242                                         // for sake of backward compatibility
    1243                                         if (plupload.typeOf(value) === 'array') {
    1244                                                 value = {
    1245                                                         mime_types: value
    1246                                                 };
    1247                                         }
     1368                                                case 'resize':
     1369                                                        if (value) {
     1370                                                                settings.resize = plupload.extend({
     1371                                                                        preserve_headers: true,
     1372                                                                        crop: false
     1373                                                                }, value);
     1374                                                        } else {
     1375                                                                settings.resize = false;
     1376                                                        }
     1377                                                        break;
    12481378
    1249                                         if (init) {
    1250                                                 plupload.extend(settings.filters, value);
    1251                                         } else {
    1252                                                 settings.filters = value;
    1253                                         }
     1379                                                case 'prevent_duplicates':
     1380                                                        settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
     1381                                                        break;
    12541382
    1255                                         // if file format filters are being updated, regenerate the matching expressions
    1256                                         if (value.mime_types) {
    1257                                                 settings.filters.mime_types.regexp = (function(filters) {
    1258                                                         var extensionsRegExp = [];
     1383                                                        // options that require reinitialisation
     1384                                                case 'container':
     1385                                                case 'browse_button':
     1386                                                case 'drop_element':
     1387                                                        value = 'container' === option ?
     1388                                                                plupload.get(value) :
     1389                                                                plupload.getAll(value);
    12591390
    1260                                                         plupload.each(filters, function(filter) {
    1261                                                                 plupload.each(filter.extensions.split(/,/), function(ext) {
    1262                                                                         if (/^\s*\*\s*$/.test(ext)) {
    1263                                                                                 extensionsRegExp.push('\\.*');
    1264                                                                         } else {
    1265                                                                                 extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
    1266                                                                         }
    1267                                                                 });
    1268                                                         });
     1391                                                case 'runtimes':
     1392                                                case 'multi_selection':
     1393                                                case 'flash_swf_url':
     1394                                                case 'silverlight_xap_url':
     1395                                                        settings[option] = value;
     1396                                                        if (!init) {
     1397                                                                reinitRequired = true;
     1398                                                        }
     1399                                                        break;
    12691400
    1270                                                         return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
    1271                                                 }(settings.filters.mime_types));
     1401                                                default:
     1402                                                        settings[option] = value;
    12721403                                        }
    1273                                         break;
    1274        
    1275                                 case 'resize':
    1276                                         if (init) {
    1277                                                 plupload.extend(settings.resize, value, {
    1278                                                         enabled: true
    1279                                                 });
    1280                                         } else {
    1281                                                 settings.resize = value;
    1282                                         }
    1283                                         break;
    12841404
    1285                                 case 'prevent_duplicates':
    1286                                         settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
    1287                                         break;
    1288 
    1289                                 // options that require reinitialisation
    1290                                 case 'container':
    1291                                 case 'browse_button':
    1292                                 case 'drop_element':
    1293                                                 value = 'container' === option
    1294                                                         ? plupload.get(value)
    1295                                                         : plupload.getAll(value)
    1296                                                         ;
    1297                                
    1298                                 case 'runtimes':
    1299                                 case 'multi_selection':
    1300                                         settings[option] = value;
    13011405                                        if (!init) {
    1302                                                 reinitRequired = true;
     1406                                                self.trigger('OptionChanged', option, value, oldValue);
    13031407                                        }
    1304                                         break;
     1408                                }
    13051409
    1306                                 default:
    1307                                         settings[option] = value;
    1308                         }
     1410                                if (typeof(option) === 'object') {
     1411                                        plupload.each(option, function(value, option) {
     1412                                                _setOption(option, value, init);
     1413                                        });
     1414                                } else {
     1415                                        _setOption(option, value, init);
     1416                                }
    13091417
    1310                         if (!init) {
    1311                                 self.trigger('OptionChanged', option, value, oldValue);
    1312                         }
    1313                 }
     1418                                if (init) {
     1419                                        // Normalize the list of required capabilities
     1420                                        settings.required_features = normalizeCaps(plupload.extend({}, settings));
    13141421
    1315                 if (typeof(option) === 'object') {
    1316                         plupload.each(option, function(value, option) {
    1317                                 _setOption(option, value, init);
    1318                         });
    1319                 } else {
    1320                         _setOption(option, value, init);
    1321                 }
     1422                                        // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
     1423                                        preferred_caps = normalizeCaps(plupload.extend({}, settings, {
     1424                                                required_features: true
     1425                                        }));
     1426                                } else if (reinitRequired) {
     1427                                        self.trigger('Destroy');
    13221428
    1323                 if (init) {
    1324                         // Normalize the list of required capabilities
    1325                         settings.required_features = normalizeCaps(plupload.extend({}, settings));
    1326 
    1327                         // Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
    1328                         preferred_caps = normalizeCaps(plupload.extend({}, settings, {
    1329                                 required_features: true
    1330                         }));
    1331                 } else if (reinitRequired) {
    1332                         self.trigger('Destroy');
    1333                        
    1334                         initControls.call(self, settings, function(inited) {
    1335                                 if (inited) {
    1336                                         self.runtime = o.Runtime.getInfo(getRUID()).type;
    1337                                         self.trigger('Init', { runtime: self.runtime });
    1338                                         self.trigger('PostInit');
    1339                                 } else {
    1340                                         self.trigger('Error', {
    1341                                                 code : plupload.INIT_ERROR,
    1342                                                 message : plupload.translate('Init error.')
     1429                                        initControls.call(self, settings, function(inited) {
     1430                                                if (inited) {
     1431                                                        self.runtime = Runtime.getInfo(getRUID()).type;
     1432                                                        self.trigger('Init', {
     1433                                                                runtime: self.runtime
     1434                                                        });
     1435                                                        self.trigger('PostInit');
     1436                                                } else {
     1437                                                        self.trigger('Error', {
     1438                                                                code: plupload.INIT_ERROR,
     1439                                                                message: plupload.translate('Init error.')
     1440                                                        });
     1441                                                }
    13431442                                        });
    13441443                                }
    1345                         });
    1346                 }
    1347         }
     1444                        }
    13481445
    13491446
    1350         // Internal event handlers
    1351         function onBeforeUpload(up, file) {
    1352                 // Generate unique target filenames
    1353                 if (up.settings.unique_names) {
    1354                         var matches = file.name.match(/\.([^.]+)$/), ext = "part";
    1355                         if (matches) {
    1356                                 ext = matches[1];
     1447                        // Internal event handlers
     1448                        function onBeforeUpload(up, file) {
     1449                                // Generate unique target filenames
     1450                                if (up.settings.unique_names) {
     1451                                        var matches = file.name.match(/\.([^.]+)$/),
     1452                                                ext = "part";
     1453                                        if (matches) {
     1454                                                ext = matches[1];
     1455                                        }
     1456                                        file.target_name = file.id + '.' + ext;
     1457                                }
    13571458                        }
    1358                         file.target_name = file.id + '.' + ext;
    1359                 }
    1360         }
    13611459
    13621460
    1363         function onUploadFile(up, file) {
    1364                 var url = up.settings.url
    1365                 , chunkSize = up.settings.chunk_size
    1366                 , retries = up.settings.max_retries
    1367                 , features = up.features
    1368                 , offset = 0
    1369                 , blob
    1370                 ;
     1461                        function onUploadFile(up, file) {
     1462                                var url = up.settings.url;
     1463                                var chunkSize = up.settings.chunk_size;
     1464                                var retries = up.settings.max_retries;
     1465                                var features = up.features;
     1466                                var offset = 0;
     1467                                var blob;
    13711468
    1372                 // make sure we start at a predictable offset
    1373                 if (file.loaded) {
    1374                         offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
    1375                 }
     1469                                var runtimeOptions = {
     1470                                        runtime_order: up.settings.runtimes,
     1471                                        required_caps: up.settings.required_features,
     1472                                        preferred_caps: preferred_caps,
     1473                                        swf_url: up.settings.flash_swf_url,
     1474                                        xap_url: up.settings.silverlight_xap_url
     1475                                };
    13761476
    1377                 function handleError() {
    1378                         if (retries-- > 0) {
    1379                                 delay(uploadNextChunk, 1000);
    1380                         } else {
    1381                                 file.loaded = offset; // reset all progress
     1477                                // make sure we start at a predictable offset
     1478                                if (file.loaded) {
     1479                                        offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
     1480                                }
    13821481
    1383                                 up.trigger('Error', {
    1384                                         code : plupload.HTTP_ERROR,
    1385                                         message : plupload.translate('HTTP Error.'),
    1386                                         file : file,
    1387                                         response : xhr.responseText,
    1388                                         status : xhr.status,
    1389                                         responseHeaders: xhr.getAllResponseHeaders()
    1390                                 });
    1391                         }
    1392                 }
     1482                                function handleError() {
     1483                                        if (retries-- > 0) {
     1484                                                delay(uploadNextChunk, 1000);
     1485                                        } else {
     1486                                                file.loaded = offset; // reset all progress
    13931487
    1394                 function uploadNextChunk() {
    1395                         var chunkBlob, formData, args = {}, curChunkSize;
     1488                                                up.trigger('Error', {
     1489                                                        code: plupload.HTTP_ERROR,
     1490                                                        message: plupload.translate('HTTP Error.'),
     1491                                                        file: file,
     1492                                                        response: xhr.responseText,
     1493                                                        status: xhr.status,
     1494                                                        responseHeaders: xhr.getAllResponseHeaders()
     1495                                                });
     1496                                        }
     1497                                }
    13961498
    1397                         // make sure that file wasn't cancelled and upload is not stopped in general
    1398                         if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
    1399                                 return;
    1400                         }
     1499                                function uploadNextChunk() {
     1500                                        var chunkBlob, args = {},
     1501                                                curChunkSize;
    14011502
    1402                         // send additional 'name' parameter only if required
    1403                         if (up.settings.send_file_name) {
    1404                                 args.name = file.target_name || file.name;
    1405                         }
     1503                                        // make sure that file wasn't cancelled and upload is not stopped in general
     1504                                        if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
     1505                                                return;
     1506                                        }
    14061507
    1407                         if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
    1408                                 curChunkSize = Math.min(chunkSize, blob.size - offset);
    1409                                 chunkBlob = blob.slice(offset, offset + curChunkSize);
    1410                         } else {
    1411                                 curChunkSize = blob.size;
    1412                                 chunkBlob = blob;
    1413                         }
     1508                                        // send additional 'name' parameter only if required
     1509                                        if (up.settings.send_file_name) {
     1510                                                args.name = file.target_name || file.name;
     1511                                        }
    14141512
    1415                         // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
    1416                         if (chunkSize && features.chunks) {
    1417                                 // Setup query string arguments
    1418                                 if (up.settings.send_chunk_number) {
    1419                                         args.chunk = Math.ceil(offset / chunkSize);
    1420                                         args.chunks = Math.ceil(blob.size / chunkSize);
    1421                                 } else { // keep support for experimental chunk format, just in case
    1422                                         args.offset = offset;
    1423                                         args.total = blob.size;
    1424                                 }
    1425                         }
     1513                                        if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
     1514                                                curChunkSize = Math.min(chunkSize, blob.size - offset);
     1515                                                chunkBlob = blob.slice(offset, offset + curChunkSize);
     1516                                        } else {
     1517                                                curChunkSize = blob.size;
     1518                                                chunkBlob = blob;
     1519                                        }
    14261520
    1427                         xhr = new o.XMLHttpRequest();
     1521                                        // If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
     1522                                        if (chunkSize && features.chunks) {
     1523                                                // Setup query string arguments
     1524                                                if (up.settings.send_chunk_number) {
     1525                                                        args.chunk = Math.ceil(offset / chunkSize);
     1526                                                        args.chunks = Math.ceil(blob.size / chunkSize);
     1527                                                } else { // keep support for experimental chunk format, just in case
     1528                                                        args.offset = offset;
     1529                                                        args.total = blob.size;
     1530                                                }
     1531                                        }
    14281532
    1429                         // Do we have upload progress support
    1430                         if (xhr.upload) {
    1431                                 xhr.upload.onprogress = function(e) {
    1432                                         file.loaded = Math.min(file.size, offset + e.loaded);
    1433                                         up.trigger('UploadProgress', file);
    1434                                 };
    1435                         }
    1436 
    1437                         xhr.onload = function() {
    1438                                 // check if upload made itself through
    1439                                 if (xhr.status >= 400) {
    1440                                         handleError();
    1441                                         return;
     1533                                        if (up.trigger('BeforeChunkUpload', file, args, chunkBlob, offset)) {
     1534                                                uploadChunk(args, chunkBlob, curChunkSize);
     1535                                        }
    14421536                                }
    14431537
    1444                                 retries = up.settings.max_retries; // reset the counter
     1538                                function uploadChunk(args, chunkBlob, curChunkSize) {
     1539                                        var formData;
    14451540
    1446                                 // Handle chunk response
    1447                                 if (curChunkSize < blob.size) {
    1448                                         chunkBlob.destroy();
     1541                                        xhr = new o.xhr.XMLHttpRequest();
    14491542
    1450                                         offset += curChunkSize;
    1451                                         file.loaded = Math.min(offset, blob.size);
     1543                                        // Do we have upload progress support
     1544                                        if (xhr.upload) {
     1545                                                xhr.upload.onprogress = function(e) {
     1546                                                        file.loaded = Math.min(file.size, offset + e.loaded);
     1547                                                        up.trigger('UploadProgress', file);
     1548                                                };
     1549                                        }
    14521550
    1453                                         up.trigger('ChunkUploaded', file, {
    1454                                                 offset : file.loaded,
    1455                                                 total : blob.size,
    1456                                                 response : xhr.responseText,
    1457                                                 status : xhr.status,
    1458                                                 responseHeaders: xhr.getAllResponseHeaders()
    1459                                         });
     1551                                        xhr.onload = function() {
     1552                                                // check if upload made itself through
     1553                                                if (xhr.status < 200 || xhr.status >= 400) {
     1554                                                        handleError();
     1555                                                        return;
     1556                                                }
    14601557
    1461                                         // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
    1462                                         if (o.Env.browser === 'Android Browser') {
    1463                                                 // doesn't harm in general, but is not required anywhere else
    1464                                                 up.trigger('UploadProgress', file);
    1465                                         }
    1466                                 } else {
    1467                                         file.loaded = file.size;
    1468                                 }
     1558                                                retries = up.settings.max_retries; // reset the counter
    14691559
    1470                                 chunkBlob = formData = null; // Free memory
     1560                                                // Handle chunk response
     1561                                                if (curChunkSize < blob.size) {
     1562                                                        chunkBlob.destroy();
    14711563
    1472                                 // Check if file is uploaded
    1473                                 if (!offset || offset >= blob.size) {
    1474                                         // If file was modified, destory the copy
    1475                                         if (file.size != file.origSize) {
    1476                                                 blob.destroy();
    1477                                                 blob = null;
    1478                                         }
     1564                                                        offset += curChunkSize;
     1565                                                        file.loaded = Math.min(offset, blob.size);
    14791566
    1480                                         up.trigger('UploadProgress', file);
     1567                                                        up.trigger('ChunkUploaded', file, {
     1568                                                                offset: file.loaded,
     1569                                                                total: blob.size,
     1570                                                                response: xhr.responseText,
     1571                                                                status: xhr.status,
     1572                                                                responseHeaders: xhr.getAllResponseHeaders()
     1573                                                        });
    14811574
    1482                                         file.status = plupload.DONE;
     1575                                                        // stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
     1576                                                        if (plupload.ua.browser === 'Android Browser') {
     1577                                                                // doesn't harm in general, but is not required anywhere else
     1578                                                                up.trigger('UploadProgress', file);
     1579                                                        }
     1580                                                } else {
     1581                                                        file.loaded = file.size;
     1582                                                }
    14831583
    1484                                         up.trigger('FileUploaded', file, {
    1485                                                 response : xhr.responseText,
    1486                                                 status : xhr.status,
    1487                                                 responseHeaders: xhr.getAllResponseHeaders()
    1488                                         });
    1489                                 } else {
    1490                                         // Still chunks left
    1491                                         delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
    1492                                 }
    1493                         };
     1584                                                chunkBlob = formData = null; // Free memory
    14941585
    1495                         xhr.onerror = function() {
    1496                                 handleError();
    1497                         };
     1586                                                // Check if file is uploaded
     1587                                                if (!offset || offset >= blob.size) {
     1588                                                        // If file was modified, destory the copy
     1589                                                        if (file.size != file.origSize) {
     1590                                                                blob.destroy();
     1591                                                                blob = null;
     1592                                                        }
    14981593
    1499                         xhr.onloadend = function() {
    1500                                 this.destroy();
    1501                                 xhr = null;
    1502                         };
     1594                                                        up.trigger('UploadProgress', file);
    15031595
    1504                         // Build multipart request
    1505                         if (up.settings.multipart && features.multipart) {
    1506                                 xhr.open("post", url, true);
     1596                                                        file.status = plupload.DONE;
     1597                                                        file.completeTimestamp = +new Date();
    15071598
    1508                                 // Set custom headers
    1509                                 plupload.each(up.settings.headers, function(value, name) {
    1510                                         xhr.setRequestHeader(name, value);
    1511                                 });
     1599                                                        up.trigger('FileUploaded', file, {
     1600                                                                response: xhr.responseText,
     1601                                                                status: xhr.status,
     1602                                                                responseHeaders: xhr.getAllResponseHeaders()
     1603                                                        });
     1604                                                } else {
     1605                                                        // Still chunks left
     1606                                                        delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
     1607                                                }
     1608                                        };
    15121609
    1513                                 formData = new o.FormData();
     1610                                        xhr.onerror = function() {
     1611                                                handleError();
     1612                                        };
    15141613
    1515                                 // Add multipart params
    1516                                 plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
    1517                                         formData.append(name, value);
    1518                                 });
     1614                                        xhr.onloadend = function() {
     1615                                                this.destroy();
     1616                                        };
    15191617
    1520                                 // Add file and send it
    1521                                 formData.append(up.settings.file_data_name, chunkBlob);
    1522                                 xhr.send(formData, {
    1523                                         runtime_order: up.settings.runtimes,
    1524                                         required_caps: up.settings.required_features,
    1525                                         preferred_caps: preferred_caps
    1526                                 });
    1527                         } else {
    1528                                 // if no multipart, send as binary stream
    1529                                 url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
     1618                                        // Build multipart request
     1619                                        if (up.settings.multipart && features.multipart) {
     1620                                                xhr.open(up.settings.http_method, url, true);
    15301621
    1531                                 xhr.open("post", url, true);
     1622                                                // Set custom headers
     1623                                                plupload.each(up.settings.headers, function(value, name) {
     1624                                                        xhr.setRequestHeader(name, value);
     1625                                                });
    15321626
    1533                                 xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
     1627                                                formData = new o.xhr.FormData();
    15341628
    1535                                 // Set custom headers
    1536                                 plupload.each(up.settings.headers, function(value, name) {
    1537                                         xhr.setRequestHeader(name, value);
    1538                                 });
     1629                                                // Add multipart params
     1630                                                plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
     1631                                                        formData.append(name, value);
     1632                                                });
    15391633
    1540                                 xhr.send(chunkBlob, {
    1541                                         runtime_order: up.settings.runtimes,
    1542                                         required_caps: up.settings.required_features,
    1543                                         preferred_caps: preferred_caps
    1544                                 });
    1545                         }
    1546                 }
     1634                                                // Add file and send it
     1635                                                formData.append(up.settings.file_data_name, chunkBlob);
     1636                                                xhr.send(formData, runtimeOptions);
     1637                                        } else {
     1638                                                // if no multipart, send as binary stream
     1639                                                url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
    15471640
    1548                 blob = file.getSource();
     1641                                                xhr.open(up.settings.http_method, url, true);
    15491642
    1550                 // Start uploading chunks
    1551                 if (up.settings.resize.enabled && runtimeCan(blob, 'send_binary_string') && !!~o.inArray(blob.type, ['image/jpeg', 'image/png'])) {
    1552                         // Resize if required
    1553                         resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
    1554                                 blob = resizedBlob;
    1555                                 file.size = resizedBlob.size;
    1556                                 uploadNextChunk();
    1557                         });
    1558                 } else {
    1559                         uploadNextChunk();
    1560                 }
    1561         }
     1643                                                // Set custom headers
     1644                                                plupload.each(up.settings.headers, function(value, name) {
     1645                                                        xhr.setRequestHeader(name, value);
     1646                                                });
    15621647
     1648                                                // do not set Content-Type, if it was defined previously (see #1203)
     1649                                                if (!xhr.hasRequestHeader('Content-Type')) {
     1650                                                        xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
     1651                                                }
    15631652
    1564         function onUploadProgress(up, file) {
    1565                 calcFile(file);
    1566         }
     1653                                                xhr.send(chunkBlob, runtimeOptions);
     1654                                        }
     1655                                }
    15671656
    15681657
    1569         function onStateChanged(up) {
    1570                 if (up.state == plupload.STARTED) {
    1571                         // Get start time to calculate bps
    1572                         startTime = (+new Date());
    1573                 } else if (up.state == plupload.STOPPED) {
    1574                         // Reset currently uploading files
    1575                         for (var i = up.files.length - 1; i >= 0; i--) {
    1576                                 if (up.files[i].status == plupload.UPLOADING) {
    1577                                         up.files[i].status = plupload.QUEUED;
    1578                                         calc();
     1658                                blob = file.getSource();
     1659
     1660                                // Start uploading chunks
     1661                                if (!plupload.isEmptyObj(up.settings.resize) && plupload.inArray(blob.type, ['image/jpeg', 'image/png']) !== -1) {
     1662                                        // Resize if required
     1663                                        resizeImage(blob, up.settings.resize, runtimeOptions, function(resizedBlob) {
     1664                                                blob = resizedBlob;
     1665                                                file.size = resizedBlob.size;
     1666                                                uploadNextChunk();
     1667                                        });
     1668                                } else {
     1669                                        uploadNextChunk();
    15791670                                }
    15801671                        }
    1581                 }
    1582         }
    15831672
    15841673
    1585         function onCancelUpload() {
    1586                 if (xhr) {
    1587                         xhr.abort();
    1588                 }
    1589         }
     1674                        function onUploadProgress(up, file) {
     1675                                calcFile(file);
     1676                        }
    15901677
    15911678
    1592         function onFileUploaded(up) {
    1593                 calc();
     1679                        function onStateChanged(up) {
     1680                                if (up.state == plupload.STARTED) {
     1681                                        // Get start time to calculate bps
     1682                                        startTime = (+new Date());
     1683                                } else if (up.state == plupload.STOPPED) {
     1684                                        // Reset currently uploading files
     1685                                        for (var i = up.files.length - 1; i >= 0; i--) {
     1686                                                if (up.files[i].status == plupload.UPLOADING) {
     1687                                                        up.files[i].status = plupload.QUEUED;
     1688                                                        calc();
     1689                                                }
     1690                                        }
     1691                                }
     1692                        }
    15941693
    1595                 // Upload next file but detach it from the error event
    1596                 // since other custom listeners might want to stop the queue
    1597                 delay(function() {
    1598                         uploadNext.call(up);
    1599                 }, 1);
    1600         }
    16011694
     1695                        function onCancelUpload() {
     1696                                if (xhr) {
     1697                                        xhr.abort();
     1698                                }
     1699                        }
    16021700
    1603         function onError(up, err) {
    1604                 if (err.code === plupload.INIT_ERROR) {
    1605                         up.destroy();
    1606                 }
    1607                 // Set failed status if an error occured on a file
    1608                 else if (err.code === plupload.HTTP_ERROR) {
    1609                         err.file.status = plupload.FAILED;
    1610                         calcFile(err.file);
    16111701
    1612                         // Upload next file but detach it from the error event
    1613                         // since other custom listeners might want to stop the queue
    1614                         if (up.state == plupload.STARTED) { // upload in progress
    1615                                 up.trigger('CancelUpload');
     1702                        function onFileUploaded(up) {
     1703                                calc();
     1704
     1705                                // Upload next file but detach it from the error event
     1706                                // since other custom listeners might want to stop the queue
    16161707                                delay(function() {
    16171708                                        uploadNext.call(up);
    16181709                                }, 1);
    16191710                        }
    1620                 }
    1621         }
    16221711
    16231712
    1624         function onDestroy(up) {
    1625                 up.stop();
     1713                        function onError(up, err) {
     1714                                if (err.code === plupload.INIT_ERROR) {
     1715                                        up.destroy();
     1716                                }
     1717                                // Set failed status if an error occured on a file
     1718                                else if (err.code === plupload.HTTP_ERROR) {
     1719                                        err.file.status = plupload.FAILED;
     1720                                        err.file.completeTimestamp = +new Date();
     1721                                        calcFile(err.file);
    16261722
    1627                 // Purge the queue
    1628                 plupload.each(files, function(file) {
    1629                         file.destroy();
    1630                 });
    1631                 files = [];
     1723                                        // Upload next file but detach it from the error event
     1724                                        // since other custom listeners might want to stop the queue
     1725                                        if (up.state == plupload.STARTED) { // upload in progress
     1726                                                up.trigger('CancelUpload');
     1727                                                delay(function() {
     1728                                                        uploadNext.call(up);
     1729                                                }, 1);
     1730                                        }
     1731                                }
     1732                        }
    16321733
    1633                 if (fileInputs.length) {
    1634                         plupload.each(fileInputs, function(fileInput) {
    1635                                 fileInput.destroy();
    1636                         });
    1637                         fileInputs = [];
    1638                 }
    16391734
    1640                 if (fileDrops.length) {
    1641                         plupload.each(fileDrops, function(fileDrop) {
    1642                                 fileDrop.destroy();
    1643                         });
    1644                         fileDrops = [];
    1645                 }
     1735                        function onDestroy(up) {
     1736                                up.stop();
    16461737
    1647                 preferred_caps = {};
    1648                 disabled = false;
    1649                 startTime = xhr = null;
    1650                 total.reset();
    1651         }
     1738                                // Purge the queue
     1739                                plupload.each(files, function(file) {
     1740                                        file.destroy();
     1741                                });
     1742                                files = [];
    16521743
     1744                                if (fileInputs.length) {
     1745                                        plupload.each(fileInputs, function(fileInput) {
     1746                                                fileInput.destroy();
     1747                                        });
     1748                                        fileInputs = [];
     1749                                }
    16531750
    1654         // Default settings
    1655         settings = {
    1656                 runtimes: o.Runtime.order,
    1657                 max_retries: 0,
    1658                 chunk_size: 0,
    1659                 multipart: true,
    1660                 multi_selection: true,
    1661                 file_data_name: 'file',
    1662                 filters: {
    1663                         mime_types: [],
    1664                         prevent_duplicates: false,
    1665                         max_file_size: 0
    1666                 },
    1667                 resize: {
    1668                         enabled: false,
    1669                         preserve_headers: true,
    1670                         crop: false
    1671                 },
    1672                 send_file_name: true,
    1673                 send_chunk_number: true
    1674         };
     1751                                if (fileDrops.length) {
     1752                                        plupload.each(fileDrops, function(fileDrop) {
     1753                                                fileDrop.destroy();
     1754                                        });
     1755                                        fileDrops = [];
     1756                                }
    16751757
    1676        
    1677         setOption.call(this, options, null, true);
     1758                                preferred_caps = {};
     1759                                disabled = false;
     1760                                startTime = xhr = null;
     1761                                total.reset();
     1762                        }
    16781763
    1679         // Inital total state
    1680         total = new plupload.QueueProgress();
    16811764
    1682         // Add public methods
    1683         plupload.extend(this, {
     1765                        // Default settings
     1766                        settings = {
     1767                                chunk_size: 0,
     1768                                file_data_name: 'file',
     1769                                filters: {
     1770                                        mime_types: [],
     1771                                        max_file_size: 0,
     1772                                        prevent_duplicates: false,
     1773                                        prevent_empty: true
     1774                                },
     1775                                flash_swf_url: 'js/Moxie.swf',
     1776                                http_method: 'POST',
     1777                                max_retries: 0,
     1778                                multipart: true,
     1779                                multi_selection: true,
     1780                                resize: false,
     1781                                runtimes: Runtime.order,
     1782                                send_file_name: true,
     1783                                send_chunk_number: true,
     1784                                silverlight_xap_url: 'js/Moxie.xap'
     1785                        };
    16841786
    1685                 /**
    1686                  * Unique id for the Uploader instance.
    1687                  *
    1688                  * @property id
    1689                  * @type String
    1690                  */
    1691                 id : uid,
    1692                 uid : uid, // mOxie uses this to differentiate between event targets
    16931787
    1694                 /**
    1695                  * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
    1696                  * These states are controlled by the stop/start methods. The default value is STOPPED.
    1697                  *
    1698                  * @property state
    1699                  * @type Number
    1700                  */
    1701                 state : plupload.STOPPED,
     1788                        setOption.call(this, options, null, true);
    17021789
    1703                 /**
    1704                  * Map of features that are available for the uploader runtime. Features will be filled
    1705                  * before the init event is called, these features can then be used to alter the UI for the end user.
    1706                  * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
    1707                  *
    1708                  * @property features
    1709                  * @type Object
    1710                  */
    1711                 features : {},
     1790                        // Inital total state
     1791                        total = new plupload.QueueProgress();
    17121792
    1713                 /**
    1714                  * Current runtime name.
    1715                  *
    1716                  * @property runtime
    1717                  * @type String
    1718                  */
    1719                 runtime : null,
     1793                        // Add public methods
     1794                        plupload.extend(this, {
    17201795
    1721                 /**
    1722                  * Current upload queue, an array of File instances.
    1723                 *
    1724                  * @property files
    1725                  * @type Array
    1726                  * @see plupload.File
    1727                  */
    1728                 files : files,
     1796                                /**
     1797                                 * Unique id for the Uploader instance.
     1798                                *
     1799                                 * @property id
     1800                                 * @type String
     1801                                 */
     1802                                id: uid,
     1803                                uid: uid, // mOxie uses this to differentiate between event targets
    17291804
    1730                 /**
    1731                  * Object with name/value settings.
    1732                  *
    1733                  * @property settings
    1734                  * @type Object
    1735                  */
    1736                 settings : settings,
     1805                                /**
     1806                                 * Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
     1807                                 * These states are controlled by the stop/start methods. The default value is STOPPED.
     1808                                 *
     1809                                 * @property state
     1810                                 * @type Number
     1811                                 */
     1812                                state: plupload.STOPPED,
    17371813
    1738                 /**
    1739                  * Total progess information. How many files has been uploaded, total percent etc.
    1740                  *
    1741                  * @property total
    1742                  * @type plupload.QueueProgress
    1743                  */
    1744                 total : total,
     1814                                /**
     1815                                 * Map of features that are available for the uploader runtime. Features will be filled
     1816                                 * before the init event is called, these features can then be used to alter the UI for the end user.
     1817                                 * Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
     1818                                 *
     1819                                 * @property features
     1820                                 * @type Object
     1821                                 */
     1822                                features: {},
    17451823
     1824                                /**
     1825                                 * Current runtime name.
     1826                                 *
     1827                                 * @property runtime
     1828                                 * @type String
     1829                                 */
     1830                                runtime: null,
    17461831
    1747                 /**
    1748                  * Initializes the Uploader instance and adds internal event listeners.
    1749                  *
    1750                  * @method init
    1751                  */
    1752                 init : function() {
    1753                         var self = this, opt, preinitOpt, err;
    1754                        
    1755                         preinitOpt = self.getOption('preinit');
    1756                         if (typeof(preinitOpt) == "function") {
    1757                                 preinitOpt(self);
    1758                         } else {
    1759                                 plupload.each(preinitOpt, function(func, name) {
    1760                                         self.bind(name, func);
    1761                                 });
    1762                         }
     1832                                /**
     1833                                 * Current upload queue, an array of File instances.
     1834                                 *
     1835                                 * @property files
     1836                                 * @type Array
     1837                                 * @see plupload.File
     1838                                 */
     1839                                files: files,
    17631840
    1764                         bindEventListeners.call(self);
     1841                                /**
     1842                                 * Object with name/value settings.
     1843                                 *
     1844                                 * @property settings
     1845                                 * @type Object
     1846                                 */
     1847                                settings: settings,
    17651848
    1766                         // Check for required options
    1767                         plupload.each(['container', 'browse_button', 'drop_element'], function(el) {
    1768                                 if (self.getOption(el) === null) {
    1769                                         err = {
    1770                                                 code : plupload.INIT_ERROR,
    1771                                                 message : plupload.translate("'%' specified, but cannot be found.")
    1772                                         }
    1773                                         return false;
    1774                                 }
    1775                         });
     1849                                /**
     1850                                 * Total progess information. How many files has been uploaded, total percent etc.
     1851                                 *
     1852                                 * @property total
     1853                                 * @type plupload.QueueProgress
     1854                                 */
     1855                                total: total,
    17761856
    1777                         if (err) {
    1778                                 return self.trigger('Error', err);
    1779                         }
    17801857
     1858                                /**
     1859                                 * Initializes the Uploader instance and adds internal event listeners.
     1860                                 *
     1861                                 * @method init
     1862                                 */
     1863                                init: function() {
     1864                                        var self = this,
     1865                                                opt, preinitOpt, err;
    17811866
    1782                         if (!settings.browse_button && !settings.drop_element) {
    1783                                 return self.trigger('Error', {
    1784                                         code : plupload.INIT_ERROR,
    1785                                         message : plupload.translate("You must specify either 'browse_button' or 'drop_element'.")
    1786                                 });
    1787                         }
     1867                                        preinitOpt = self.getOption('preinit');
     1868                                        if (typeof(preinitOpt) == "function") {
     1869                                                preinitOpt(self);
     1870                                        } else {
     1871                                                plupload.each(preinitOpt, function(func, name) {
     1872                                                        self.bind(name, func);
     1873                                                });
     1874                                        }
    17881875
     1876                                        bindEventListeners.call(self);
    17891877
    1790                         initControls.call(self, settings, function(inited) {
    1791                                 var initOpt = self.getOption('init');
    1792                                 if (typeof(initOpt) == "function") {
    1793                                         initOpt(self);
    1794                                 } else {
    1795                                         plupload.each(initOpt, function(func, name) {
    1796                                                 self.bind(name, func);
     1878                                        // Check for required options
     1879                                        plupload.each(['container', 'browse_button', 'drop_element'], function(el) {
     1880                                                if (self.getOption(el) === null) {
     1881                                                        err = {
     1882                                                                code: plupload.INIT_ERROR,
     1883                                                                message: plupload.sprintf(plupload.translate("%s specified, but cannot be found."), el)
     1884                                                        }
     1885                                                        return false;
     1886                                                }
    17971887                                        });
    1798                                 }
    17991888
    1800                                 if (inited) {
    1801                                         self.runtime = o.Runtime.getInfo(getRUID()).type;
    1802                                         self.trigger('Init', { runtime: self.runtime });
    1803                                         self.trigger('PostInit');
    1804                                 } else {
    1805                                         self.trigger('Error', {
    1806                                                 code : plupload.INIT_ERROR,
    1807                                                 message : plupload.translate('Init error.')
    1808                                         });
    1809                                 }
    1810                         });
    1811                 },
     1889                                        if (err) {
     1890                                                return self.trigger('Error', err);
     1891                                        }
    18121892
    1813                 /**
    1814                  * Set the value for the specified option(s).
    1815                  *
    1816                  * @method setOption
    1817                  * @since 2.1
    1818                  * @param {String|Object} option Name of the option to change or the set of key/value pairs
    1819                  * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
    1820                  */
    1821                 setOption: function(option, value) {
    1822                         setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
    1823                 },
    18241893
    1825                 /**
    1826                  * Get the value for the specified option or the whole configuration, if not specified.
    1827                  *
    1828                  * @method getOption
    1829                  * @since 2.1
    1830                  * @param {String} [option] Name of the option to get
    1831                  * @return {Mixed} Value for the option or the whole set
    1832                  */
    1833                 getOption: function(option) {
    1834                         if (!option) {
    1835                                 return settings;
    1836                         }
    1837                         return settings[option];
    1838                 },
     1894                                        if (!settings.browse_button && !settings.drop_element) {
     1895                                                return self.trigger('Error', {
     1896                                                        code: plupload.INIT_ERROR,
     1897                                                        message: plupload.translate("You must specify either browse_button or drop_element.")
     1898                                                });
     1899                                        }
    18391900
    1840                 /**
    1841                  * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
    1842                  * This would for example reposition flash/silverlight shims on the page.
    1843                  *
    1844                  * @method refresh
    1845                  */
    1846                 refresh : function() {
    1847                         if (fileInputs.length) {
    1848                                 plupload.each(fileInputs, function(fileInput) {
    1849                                         fileInput.trigger('Refresh');
    1850                                 });
    1851                         }
    1852                         this.trigger('Refresh');
    1853                 },
    18541901
    1855                 /**
    1856                  * Starts uploading the queued files.
    1857                  *
    1858                  * @method start
    1859                  */
    1860                 start : function() {
    1861                         if (this.state != plupload.STARTED) {
    1862                                 this.state = plupload.STARTED;
    1863                                 this.trigger('StateChanged');
     1902                                        initControls.call(self, settings, function(inited) {
     1903                                                var initOpt = self.getOption('init');
     1904                                                if (typeof(initOpt) == "function") {
     1905                                                        initOpt(self);
     1906                                                } else {
     1907                                                        plupload.each(initOpt, function(func, name) {
     1908                                                                self.bind(name, func);
     1909                                                        });
     1910                                                }
    18641911
    1865                                 uploadNext.call(this);
    1866                         }
    1867                 },
     1912                                                if (inited) {
     1913                                                        self.runtime = Runtime.getInfo(getRUID()).type;
     1914                                                        self.trigger('Init', {
     1915                                                                runtime: self.runtime
     1916                                                        });
     1917                                                        self.trigger('PostInit');
     1918                                                } else {
     1919                                                        self.trigger('Error', {
     1920                                                                code: plupload.INIT_ERROR,
     1921                                                                message: plupload.translate('Init error.')
     1922                                                        });
     1923                                                }
     1924                                        });
     1925                                },
    18681926
    1869                 /**
    1870                  * Stops the upload of the queued files.
    1871                  *
    1872                  * @method stop
    1873                  */
    1874                 stop : function() {
    1875                         if (this.state != plupload.STOPPED) {
    1876                                 this.state = plupload.STOPPED;
    1877                                 this.trigger('StateChanged');
    1878                                 this.trigger('CancelUpload');
    1879                         }
    1880                 },
     1927                                /**
     1928                                 * Set the value for the specified option(s).
     1929                                 *
     1930                                 * @method setOption
     1931                                 * @since 2.1
     1932                                 * @param {String|Object} option Name of the option to change or the set of key/value pairs
     1933                                 * @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
     1934                                 */
     1935                                setOption: function(option, value) {
     1936                                        setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
     1937                                },
    18811938
     1939                                /**
     1940                                 * Get the value for the specified option or the whole configuration, if not specified.
     1941                                 *
     1942                                 * @method getOption
     1943                                 * @since 2.1
     1944                                 * @param {String} [option] Name of the option to get
     1945                                 * @return {Mixed} Value for the option or the whole set
     1946                                 */
     1947                                getOption: function(option) {
     1948                                        if (!option) {
     1949                                                return settings;
     1950                                        }
     1951                                        return settings[option];
     1952                                },
    18821953
    1883                 /**
    1884                  * Disables/enables browse button on request.
    1885                  *
    1886                  * @method disableBrowse
    1887                  * @param {Boolean} disable Whether to disable or enable (default: true)
    1888                  */
    1889                 disableBrowse : function() {
    1890                         disabled = arguments[0] !== undef ? arguments[0] : true;
     1954                                /**
     1955                                 * Refreshes the upload instance by dispatching out a refresh event to all runtimes.
     1956                                 * This would for example reposition flash/silverlight shims on the page.
     1957                                 *
     1958                                 * @method refresh
     1959                                 */
     1960                                refresh: function() {
     1961                                        if (fileInputs.length) {
     1962                                                plupload.each(fileInputs, function(fileInput) {
     1963                                                        fileInput.trigger('Refresh');
     1964                                                });
     1965                                        }
     1966                                        this.trigger('Refresh');
     1967                                },
    18911968
    1892                         if (fileInputs.length) {
    1893                                 plupload.each(fileInputs, function(fileInput) {
    1894                                         fileInput.disable(disabled);
    1895                                 });
    1896                         }
     1969                                /**
     1970                                 * Starts uploading the queued files.
     1971                                 *
     1972                                 * @method start
     1973                                 */
     1974                                start: function() {
     1975                                        if (this.state != plupload.STARTED) {
     1976                                                this.state = plupload.STARTED;
     1977                                                this.trigger('StateChanged');
    18971978
    1898                         this.trigger('DisableBrowse', disabled);
    1899                 },
     1979                                                uploadNext.call(this);
     1980                                        }
     1981                                },
    19001982
    1901                 /**
    1902                  * Returns the specified file object by id.
    1903                  *
    1904                  * @method getFile
    1905                  * @param {String} id File id to look for.
    1906                  * @return {plupload.File} File object or undefined if it wasn't found;
    1907                  */
    1908                 getFile : function(id) {
    1909                         var i;
    1910                         for (i = files.length - 1; i >= 0; i--) {
    1911                                 if (files[i].id === id) {
    1912                                         return files[i];
    1913                                 }
    1914                         }
    1915                 },
     1983                                /**
     1984                                 * Stops the upload of the queued files.
     1985                                 *
     1986                                 * @method stop
     1987                                 */
     1988                                stop: function() {
     1989                                        if (this.state != plupload.STOPPED) {
     1990                                                this.state = plupload.STOPPED;
     1991                                                this.trigger('StateChanged');
     1992                                                this.trigger('CancelUpload');
     1993                                        }
     1994                                },
    19161995
    1917                 /**
    1918                  * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
    1919                  * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
    1920                  * if any files were added to the queue. Otherwise nothing happens.
    1921                  *
    1922                  * @method addFile
    1923                  * @since 2.0
    1924                  * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
    1925                  * @param {String} [fileName] If specified, will be used as a name for the file
    1926                  */
    1927                 addFile : function(file, fileName) {
    1928                         var self = this
    1929                         , queue = []
    1930                         , filesAdded = []
    1931                         , ruid
    1932                         ;
    19331996
    1934                         function filterFile(file, cb) {
    1935                                 var queue = [];
    1936                                 o.each(self.settings.filters, function(rule, name) {
    1937                                         if (fileFilters[name]) {
    1938                                                 queue.push(function(cb) {
    1939                                                         fileFilters[name].call(self, rule, file, function(res) {
    1940                                                                 cb(!res);
    1941                                                         });
     1997                                /**
     1998                                 * Disables/enables browse button on request.
     1999                                 *
     2000                                 * @method disableBrowse
     2001                                 * @param {Boolean} disable Whether to disable or enable (default: true)
     2002                                 */
     2003                                disableBrowse: function() {
     2004                                        disabled = arguments[0] !== undef ? arguments[0] : true;
     2005
     2006                                        if (fileInputs.length) {
     2007                                                plupload.each(fileInputs, function(fileInput) {
     2008                                                        fileInput.disable(disabled);
    19422009                                                });
    19432010                                        }
    1944                                 });
    1945                                 o.inSeries(queue, cb);
    1946                         }
    19472011
    1948                         /**
    1949                          * @method resolveFile
    1950                          * @private
    1951                          * @param {o.File|o.Blob|plupload.File|File|Blob|input[type="file"]} file
    1952                          */
    1953                         function resolveFile(file) {
    1954                                 var type = o.typeOf(file);
     2012                                        this.trigger('DisableBrowse', disabled);
     2013                                },
    19552014
    1956                                 // o.File
    1957                                 if (file instanceof o.File) {
    1958                                         if (!file.ruid && !file.isDetached()) {
    1959                                                 if (!ruid) { // weird case
    1960                                                         return false;
     2015                                /**
     2016                                 * Returns the specified file object by id.
     2017                                 *
     2018                                 * @method getFile
     2019                                 * @param {String} id File id to look for.
     2020                                 * @return {plupload.File} File object or undefined if it wasn't found;
     2021                                 */
     2022                                getFile: function(id) {
     2023                                        var i;
     2024                                        for (i = files.length - 1; i >= 0; i--) {
     2025                                                if (files[i].id === id) {
     2026                                                        return files[i];
    19612027                                                }
    1962                                                 file.ruid = ruid;
    1963                                                 file.connectRuntime(ruid);
    19642028                                        }
    1965                                         resolveFile(new plupload.File(file));
    1966                                 }
    1967                                 // o.Blob
    1968                                 else if (file instanceof o.Blob) {
    1969                                         resolveFile(file.getSource());
    1970                                         file.destroy();
    1971                                 }
    1972                                 // plupload.File - final step for other branches
    1973                                 else if (file instanceof plupload.File) {
    1974                                         if (fileName) {
    1975                                                 file.name = fileName;
     2029                                },
     2030
     2031                                /**
     2032                                 * Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
     2033                                 * instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
     2034                                 * if any files were added to the queue. Otherwise nothing happens.
     2035                                 *
     2036                                 * @method addFile
     2037                                 * @since 2.0
     2038                                 * @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
     2039                                 * @param {String} [fileName] If specified, will be used as a name for the file
     2040                                 */
     2041                                addFile: function(file, fileName) {
     2042                                        var self = this,
     2043                                                queue = [],
     2044                                                filesAdded = [],
     2045                                                ruid;
     2046
     2047                                        function filterFile(file, cb) {
     2048                                                var queue = [];
     2049                                                plupload.each(self.settings.filters, function(rule, name) {
     2050                                                        if (fileFilters[name]) {
     2051                                                                queue.push(function(cb) {
     2052                                                                        fileFilters[name].call(self, rule, file, function(res) {
     2053                                                                                cb(!res);
     2054                                                                        });
     2055                                                                });
     2056                                                        }
     2057                                                });
     2058                                                plupload.inSeries(queue, cb);
    19762059                                        }
    1977                                        
    1978                                         queue.push(function(cb) {
    1979                                                 // run through the internal and user-defined filters, if any
    1980                                                 filterFile(file, function(err) {
    1981                                                         if (!err) {
    1982                                                                 // make files available for the filters by updating the main queue directly
    1983                                                                 files.push(file);
    1984                                                                 // collect the files that will be passed to FilesAdded event
    1985                                                                 filesAdded.push(file);
    19862060
    1987                                                                 self.trigger("FileFiltered", file);
     2061                                        /**
     2062                                         * @method resolveFile
     2063                                         * @private
     2064                                         * @param {moxie.file.File|moxie.file.Blob|plupload.File|File|Blob|input[type="file"]} file
     2065                                         */
     2066                                        function resolveFile(file) {
     2067                                                var type = plupload.typeOf(file);
     2068
     2069                                                // moxie.file.File
     2070                                                if (file instanceof o.file.File) {
     2071                                                        if (!file.ruid && !file.isDetached()) {
     2072                                                                if (!ruid) { // weird case
     2073                                                                        return false;
     2074                                                                }
     2075                                                                file.ruid = ruid;
     2076                                                                file.connectRuntime(ruid);
    19882077                                                        }
    1989                                                         delay(cb, 1); // do not build up recursions or eventually we might hit the limits
     2078                                                        resolveFile(new plupload.File(file));
     2079                                                }
     2080                                                // moxie.file.Blob
     2081                                                else if (file instanceof o.file.Blob) {
     2082                                                        resolveFile(file.getSource());
     2083                                                        file.destroy();
     2084                                                }
     2085                                                // plupload.File - final step for other branches
     2086                                                else if (file instanceof plupload.File) {
     2087                                                        if (fileName) {
     2088                                                                file.name = fileName;
     2089                                                        }
     2090
     2091                                                        queue.push(function(cb) {
     2092                                                                // run through the internal and user-defined filters, if any
     2093                                                                filterFile(file, function(err) {
     2094                                                                        if (!err) {
     2095                                                                                // make files available for the filters by updating the main queue directly
     2096                                                                                files.push(file);
     2097                                                                                // collect the files that will be passed to FilesAdded event
     2098                                                                                filesAdded.push(file);
     2099
     2100                                                                                self.trigger("FileFiltered", file);
     2101                                                                        }
     2102                                                                        delay(cb, 1); // do not build up recursions or eventually we might hit the limits
     2103                                                                });
     2104                                                        });
     2105                                                }
     2106                                                // native File or blob
     2107                                                else if (plupload.inArray(type, ['file', 'blob']) !== -1) {
     2108                                                        resolveFile(new o.file.File(null, file));
     2109                                                }
     2110                                                // input[type="file"]
     2111                                                else if (type === 'node' && plupload.typeOf(file.files) === 'filelist') {
     2112                                                        // if we are dealing with input[type="file"]
     2113                                                        plupload.each(file.files, resolveFile);
     2114                                                }
     2115                                                // mixed array of any supported types (see above)
     2116                                                else if (type === 'array') {
     2117                                                        fileName = null; // should never happen, but unset anyway to avoid funny situations
     2118                                                        plupload.each(file, resolveFile);
     2119                                                }
     2120                                        }
     2121
     2122                                        ruid = getRUID();
     2123
     2124                                        resolveFile(file);
     2125
     2126                                        if (queue.length) {
     2127                                                plupload.inSeries(queue, function() {
     2128                                                        // if any files left after filtration, trigger FilesAdded
     2129                                                        if (filesAdded.length) {
     2130                                                                self.trigger("FilesAdded", filesAdded);
     2131                                                        }
    19902132                                                });
    1991                                         });
    1992                                 }
    1993                                 // native File or blob
    1994                                 else if (o.inArray(type, ['file', 'blob']) !== -1) {
    1995                                         resolveFile(new o.File(null, file));
    1996                                 }
    1997                                 // input[type="file"]
    1998                                 else if (type === 'node' && o.typeOf(file.files) === 'filelist') {
    1999                                         // if we are dealing with input[type="file"]
    2000                                         o.each(file.files, resolveFile);
    2001                                 }
    2002                                 // mixed array of any supported types (see above)
    2003                                 else if (type === 'array') {
    2004                                         fileName = null; // should never happen, but unset anyway to avoid funny situations
    2005                                         o.each(file, resolveFile);
    2006                                 }
    2007                         }
     2133                                        }
     2134                                },
    20082135
    2009                         ruid = getRUID();
    2010                        
    2011                         resolveFile(file);
     2136                                /**
     2137                                 * Removes a specific file.
     2138                                 *
     2139                                 * @method removeFile
     2140                                 * @param {plupload.File|String} file File to remove from queue.
     2141                                 */
     2142                                removeFile: function(file) {
     2143                                        var id = typeof(file) === 'string' ? file : file.id;
    20122144
    2013                         if (queue.length) {
    2014                                 o.inSeries(queue, function() {
    2015                                         // if any files left after filtration, trigger FilesAdded
    2016                                         if (filesAdded.length) {
    2017                                                 self.trigger("FilesAdded", filesAdded);
     2145                                        for (var i = files.length - 1; i >= 0; i--) {
     2146                                                if (files[i].id === id) {
     2147                                                        return this.splice(i, 1)[0];
     2148                                                }
    20182149                                        }
    2019                                 });
    2020                         }
    2021                 },
     2150                                },
    20222151
    2023                 /**
    2024                  * Removes a specific file.
    2025                  *
    2026                  * @method removeFile
    2027                  * @param {plupload.File|String} file File to remove from queue.
    2028                  */
    2029                 removeFile : function(file) {
    2030                         var id = typeof(file) === 'string' ? file : file.id;
     2152                                /**
     2153                                 * Removes part of the queue and returns the files removed. This will also trigger the
     2154                                 * FilesRemoved and QueueChanged events.
     2155                                 *
     2156                                 * @method splice
     2157                                 * @param {Number} [start=0] Start index to remove from.
     2158                                 * @param {Number} [length] Number of files to remove (defaults to number of files in the queue).
     2159                                 * @return {Array} Array of files that was removed.
     2160                                 */
     2161                                splice: function(start, length) {
     2162                                        // Splice and trigger events
     2163                                        var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
    20312164
    2032                         for (var i = files.length - 1; i >= 0; i--) {
    2033                                 if (files[i].id === id) {
    2034                                         return this.splice(i, 1)[0];
    2035                                 }
    2036                         }
    2037                 },
     2165                                        // if upload is in progress we need to stop it and restart after files are removed
     2166                                        var restartRequired = false;
     2167                                        if (this.state == plupload.STARTED) { // upload in progress
     2168                                                plupload.each(removed, function(file) {
     2169                                                        if (file.status === plupload.UPLOADING) {
     2170                                                                restartRequired = true; // do not restart, unless file that is being removed is uploading
     2171                                                                return false;
     2172                                                        }
     2173                                                });
    20382174
    2039                 /**
    2040                  * Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events.
    2041                  *
    2042                  * @method splice
    2043                  * @param {Number} start (Optional) Start index to remove from.
    2044                  * @param {Number} length (Optional) Lengh of items to remove.
    2045                  * @return {Array} Array of files that was removed.
    2046                  */
    2047                 splice : function(start, length) {
    2048                         // Splice and trigger events
    2049                         var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
     2175                                                if (restartRequired) {
     2176                                                        this.stop();
     2177                                                }
     2178                                        }
    20502179
    2051                         // if upload is in progress we need to stop it and restart after files are removed
    2052                         var restartRequired = false;
    2053                         if (this.state == plupload.STARTED) { // upload in progress
    2054                                 plupload.each(removed, function(file) {
    2055                                         if (file.status === plupload.UPLOADING) {
    2056                                                 restartRequired = true; // do not restart, unless file that is being removed is uploading
    2057                                                 return false;
     2180                                        this.trigger("FilesRemoved", removed);
     2181
     2182                                        // Dispose any resources allocated by those files
     2183                                        plupload.each(removed, function(file) {
     2184                                                file.destroy();
     2185                                        });
     2186
     2187                                        if (restartRequired) {
     2188                                                this.start();
    20582189                                        }
    2059                                 });
    2060                                
    2061                                 if (restartRequired) {
    2062                                         this.stop();
    2063                                 }
    2064                         }
    20652190
    2066                         this.trigger("FilesRemoved", removed);
     2191                                        return removed;
     2192                                },
    20672193
    2068                         // Dispose any resources allocated by those files
    2069                         plupload.each(removed, function(file) {
    2070                                 file.destroy();
    2071                         });
    2072                        
    2073                         if (restartRequired) {
    2074                                 this.start();
    2075                         }
     2194                                /**
     2195                                Dispatches the specified event name and its arguments to all listeners.
    20762196
    2077                         return removed;
    2078                 },
     2197                                @method trigger
     2198                                @param {String} name Event name to fire.
     2199                                @param {Object..} Multiple arguments to pass along to the listener functions.
     2200                                */
    20792201
    2080                 /**
    2081                 Dispatches the specified event name and its arguments to all listeners.
     2202                                // override the parent method to match Plupload-like event logic
     2203                                dispatchEvent: function(type) {
     2204                                        var list, args, result;
    20822205
    2083                 @method trigger
    2084                 @param {String} name Event name to fire.
    2085                 @param {Object..} Multiple arguments to pass along to the listener functions.
    2086                 */
     2206                                        type = type.toLowerCase();
    20872207
    2088                 // override the parent method to match Plupload-like event logic
    2089                 dispatchEvent: function(type) {
    2090                         var list, args, result;
    2091                                                
    2092                         type = type.toLowerCase();
    2093                                                        
    2094                         list = this.hasEventListener(type);
     2208                                        list = this.hasEventListener(type);
    20952209
    2096                         if (list) {
    2097                                 // sort event list by priority
    2098                                 list.sort(function(a, b) { return b.priority - a.priority; });
    2099                                
    2100                                 // first argument should be current plupload.Uploader instance
    2101                                 args = [].slice.call(arguments);
    2102                                 args.shift();
    2103                                 args.unshift(this);
     2210                                        if (list) {
     2211                                                // sort event list by priority
     2212                                                list.sort(function(a, b) {
     2213                                                        return b.priority - a.priority;
     2214                                                });
    21042215
    2105                                 for (var i = 0; i < list.length; i++) {
    2106                                         // Fire event, break chain if false is returned
    2107                                         if (list[i].fn.apply(list[i].scope, args) === false) {
    2108                                                 return false;
     2216                                                // first argument should be current plupload.Uploader instance
     2217                                                args = [].slice.call(arguments);
     2218                                                args.shift();
     2219                                                args.unshift(this);
     2220
     2221                                                for (var i = 0; i < list.length; i++) {
     2222                                                        // Fire event, break chain if false is returned
     2223                                                        if (list[i].fn.apply(list[i].scope, args) === false) {
     2224                                                                return false;
     2225                                                        }
     2226                                                }
    21092227                                        }
    2110                                 }
    2111                         }
    2112                         return true;
    2113                 },
     2228                                        return true;
     2229                                },
    21142230
    2115                 /**
    2116                 Check whether uploader has any listeners to the specified event.
     2231                                /**
     2232                                Check whether uploader has any listeners to the specified event.
    21172233
    2118                 @method hasEventListener
    2119                 @param {String} name Event name to check for.
    2120                 */
     2234                                @method hasEventListener
     2235                                @param {String} name Event name to check for.
     2236                                */
    21212237
    21222238
    2123                 /**
    2124                 Adds an event listener by name.
     2239                                /**
     2240                                Adds an event listener by name.
    21252241
    2126                 @method bind
    2127                 @param {String} name Event name to listen for.
    2128                 @param {function} fn Function to call ones the event gets fired.
    2129                 @param {Object} [scope] Optional scope to execute the specified function in.
    2130                 @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
    2131                 */
    2132                 bind: function(name, fn, scope, priority) {
    2133                         // adapt moxie EventTarget style to Plupload-like
    2134                         plupload.Uploader.prototype.bind.call(this, name, fn, priority, scope);
    2135                 },
     2242                                @method bind
     2243                                @param {String} name Event name to listen for.
     2244                                @param {function} fn Function to call ones the event gets fired.
     2245                                @param {Object} [scope] Optional scope to execute the specified function in.
     2246                                @param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
     2247                                */
     2248                                bind: function(name, fn, scope, priority) {
     2249                                        // adapt moxie EventTarget style to Plupload-like
     2250                                        plupload.Uploader.prototype.bind.call(this, name, fn, priority, scope);
     2251                                },
    21362252
    2137                 /**
    2138                 Removes the specified event listener.
     2253                                /**
     2254                                Removes the specified event listener.
    21392255
    2140                 @method unbind
    2141                 @param {String} name Name of event to remove.
    2142                 @param {function} fn Function to remove from listener.
    2143                 */
     2256                                @method unbind
     2257                                @param {String} name Name of event to remove.
     2258                                @param {function} fn Function to remove from listener.
     2259                                */
    21442260
    2145                 /**
    2146                 Removes all event listeners.
     2261                                /**
     2262                                Removes all event listeners.
    21472263
    2148                 @method unbindAll
    2149                 */
     2264                                @method unbindAll
     2265                                */
    21502266
    21512267
     2268                                /**
     2269                                 * Destroys Plupload instance and cleans after itself.
     2270                                 *
     2271                                 * @method destroy
     2272                                 */
     2273                                destroy: function() {
     2274                                        this.trigger('Destroy');
     2275                                        settings = total = null; // purge these exclusively
     2276                                        this.unbindAll();
     2277                                }
     2278                        });
     2279                };
     2280
     2281                plupload.Uploader.prototype = o.core.EventTarget.instance;
     2282
    21522283                /**
    2153                  * Destroys Plupload instance and cleans after itself.
     2284                 * Constructs a new file instance.
    21542285                 *
    2155                  * @method destroy
     2286                 * @class File
     2287                 * @constructor
     2288                 *
     2289                 * @param {Object} file Object containing file properties
     2290                 * @param {String} file.name Name of the file.
     2291                 * @param {Number} file.size File size.
    21562292                 */
    2157                 destroy : function() {
    2158                         this.trigger('Destroy');
    2159                         settings = total = null; // purge these exclusively
    2160                         this.unbindAll();
    2161                 }
    2162         });
    2163 };
     2293                plupload.File = (function() {
     2294                        var filepool = {};
    21642295
    2165 plupload.Uploader.prototype = o.EventTarget.instance;
     2296                        function PluploadFile(file) {
    21662297
    2167 /**
    2168  * Constructs a new file instance.
    2169  *
    2170  * @class File
    2171  * @constructor
    2172  *
    2173  * @param {Object} file Object containing file properties
    2174  * @param {String} file.name Name of the file.
    2175  * @param {Number} file.size File size.
    2176  */
    2177 plupload.File = (function() {
    2178         var filepool = {};
     2298                                plupload.extend(this, {
    21792299
    2180         function PluploadFile(file) {
     2300                                        /**
     2301                                         * File id this is a globally unique id for the specific file.
     2302                                         *
     2303                                         * @property id
     2304                                         * @type String
     2305                                         */
     2306                                        id: plupload.guid(),
    21812307
    2182                 plupload.extend(this, {
     2308                                        /**
     2309                                         * File name for example "myfile.gif".
     2310                                         *
     2311                                         * @property name
     2312                                         * @type String
     2313                                         */
     2314                                        name: file.name || file.fileName,
    21832315
    2184                         /**
    2185                          * File id this is a globally unique id for the specific file.
    2186                         *
    2187                          * @property id
    2188                         * @type String
    2189                         */
    2190                         id: plupload.guid(),
     2316                                        /**
     2317                                         * File type, `e.g image/jpeg`
     2318                                        *
     2319                                         * @property type
     2320                                        * @type String
     2321                                        */
     2322                                        type: file.type || '',
    21912323
    2192                         /**
    2193                          * File name for example "myfile.gif".
    2194                          *
    2195                          * @property name
    2196                          * @type String
    2197                          */
    2198                         name: file.name || file.fileName,
     2324                                        /**
     2325                                         * Relative path to the file inside a directory
     2326                                         *
     2327                                         * @property relativePath
     2328                                         * @type String
     2329                                         * @default ''
     2330                                         */
     2331                                        relativePath: file.relativePath || '',
    21992332
    2200                         /**
    2201                          * File type, `e.g image/jpeg`
    2202                         *
    2203                          * @property type
    2204                          * @type String
    2205                         */
    2206                         type: file.type || '',
     2333                                        /**
     2334                                         * File size in bytes (may change after client-side manupilation).
     2335                                        *
     2336                                         * @property size
     2337                                         * @type Number
     2338                                        */
     2339                                        size: file.fileSize || file.size,
    22072340
     2341                                        /**
     2342                                         * Original file size in bytes.
     2343                                         *
     2344                                         * @property origSize
     2345                                         * @type Number
     2346                                         */
     2347                                        origSize: file.fileSize || file.size,
     2348
     2349                                        /**
     2350                                         * Number of bytes uploaded of the files total size.
     2351                                         *
     2352                                         * @property loaded
     2353                                         * @type Number
     2354                                         */
     2355                                        loaded: 0,
     2356
     2357                                        /**
     2358                                         * Number of percentage uploaded of the file.
     2359                                         *
     2360                                         * @property percent
     2361                                         * @type Number
     2362                                         */
     2363                                        percent: 0,
     2364
     2365                                        /**
     2366                                         * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
     2367                                         *
     2368                                         * @property status
     2369                                         * @type Number
     2370                                         * @see plupload
     2371                                         */
     2372                                        status: plupload.QUEUED,
     2373
     2374                                        /**
     2375                                         * Date of last modification.
     2376                                         *
     2377                                         * @property lastModifiedDate
     2378                                         * @type {String}
     2379                                         */
     2380                                        lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
     2381
     2382
     2383                                        /**
     2384                                         * Set when file becomes plupload.DONE or plupload.FAILED. Is used to calculate proper plupload.QueueProgress.bytesPerSec.
     2385                                         * @private
     2386                                         * @property completeTimestamp
     2387                                         * @type {Number}
     2388                                         */
     2389                                        completeTimestamp: 0,
     2390
     2391                                        /**
     2392                                         * Returns native window.File object, when it's available.
     2393                                         *
     2394                                         * @method getNative
     2395                                         * @return {window.File} or null, if plupload.File is of different origin
     2396                                         */
     2397                                        getNative: function() {
     2398                                                var file = this.getSource().getSource();
     2399                                                return plupload.inArray(plupload.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
     2400                                        },
     2401
     2402                                        /**
     2403                                         * Returns mOxie.File - unified wrapper object that can be used across runtimes.
     2404                                         *
     2405                                         * @method getSource
     2406                                         * @return {mOxie.File} or null
     2407                                         */
     2408                                        getSource: function() {
     2409                                                if (!filepool[this.id]) {
     2410                                                        return null;
     2411                                                }
     2412                                                return filepool[this.id];
     2413                                        },
     2414
     2415                                        /**
     2416                                         * Destroys plupload.File object.
     2417                                         *
     2418                                         * @method destroy
     2419                                         */
     2420                                        destroy: function() {
     2421                                                var src = this.getSource();
     2422                                                if (src) {
     2423                                                        src.destroy();
     2424                                                        delete filepool[this.id];
     2425                                                }
     2426                                        }
     2427                                });
     2428
     2429                                filepool[this.id] = file;
     2430                        }
     2431
     2432                        return PluploadFile;
     2433                }());
     2434
     2435
     2436                /**
     2437                 * Constructs a queue progress.
     2438                 *
     2439                 * @class QueueProgress
     2440                 * @constructor
     2441                 */
     2442                plupload.QueueProgress = function() {
     2443                        var self = this; // Setup alias for self to reduce code size when it's compressed
     2444
    22082445                        /**
    2209                          * File size in bytes (may change after client-side manupilation).
     2446                         * Total queue file size.
    22102447                         *
    22112448                         * @property size
    22122449                         * @type Number
    22132450                         */
    2214                         size: file.size || file.fileSize,
     2451                        self.size = 0;
    22152452
    22162453                        /**
    2217                          * Original file size in bytes.
     2454                         * Total bytes uploaded.
    22182455                         *
    2219                          * @property origSize
     2456                         * @property loaded
    22202457                         * @type Number
    22212458                         */
    2222                         origSize: file.size || file.fileSize,
     2459                        self.loaded = 0;
    22232460
    22242461                        /**
    2225                          * Number of bytes uploaded of the files total size.
     2462                         * Number of files uploaded.
    22262463                         *
    2227                          * @property loaded
     2464                         * @property uploaded
    22282465                         * @type Number
    22292466                         */
    2230                         loaded: 0,
     2467                        self.uploaded = 0;
    22312468
    22322469                        /**
    2233                          * Number of percentage uploaded of the file.
     2470                         * Number of files failed to upload.
    22342471                         *
    2235                          * @property percent
     2472                         * @property failed
    22362473                         * @type Number
    22372474                         */
    2238                         percent: 0,
     2475                        self.failed = 0;
    22392476
    22402477                        /**
    2241                          * Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
     2478                         * Number of files yet to be uploaded.
    22422479                         *
    2243                          * @property status
     2480                         * @property queued
    22442481                         * @type Number
    2245                          * @see plupload
    22462482                         */
    2247                         status: plupload.QUEUED,
     2483                        self.queued = 0;
    22482484
    22492485                        /**
    2250                          * Date of last modification.
     2486                         * Total percent of the uploaded bytes.
    22512487                         *
    2252                          * @property lastModifiedDate
    2253                          * @type {String}
     2488                         * @property percent
     2489                         * @type Number
    22542490                         */
    2255                         lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
     2491                        self.percent = 0;
    22562492
    22572493                        /**
    2258                          * Returns native window.File object, when it's available.
     2494                         * Bytes uploaded per second.
    22592495                         *
    2260                          * @method getNative
    2261                          * @return {window.File} or null, if plupload.File is of different origin
     2496                         * @property bytesPerSec
     2497                         * @type Number
    22622498                         */
    2263                         getNative: function() {
    2264                                 var file = this.getSource().getSource();
    2265                                 return o.inArray(o.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
    2266                         },
     2499                        self.bytesPerSec = 0;
    22672500
    22682501                        /**
    2269                          * Returns mOxie.File - unified wrapper object that can be used across runtimes.
     2502                         * Resets the progress to its initial values.
    22702503                         *
    2271                          * @method getSource
    2272                          * @return {mOxie.File} or null
     2504                         * @method reset
    22732505                         */
    2274                         getSource: function() {
    2275                                 if (!filepool[this.id]) {
    2276                                         return null;
    2277                                 }
    2278                                 return filepool[this.id];
    2279                         },
     2506                        self.reset = function() {
     2507                                self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
     2508                        };
     2509                };
    22802510
    2281                         /**
    2282                          * Destroys plupload.File object.
    2283                          *
    2284                          * @method destroy
    2285                          */
    2286                         destroy: function() {
    2287                                 var src = this.getSource();
    2288                                 if (src) {
    2289                                         src.destroy();
    2290                                         delete filepool[this.id];
    2291                                 }
    2292                         }
    2293                 });
     2511                exports.plupload = plupload;
    22942512
    2295                 filepool[this.id] = file;
    2296         }
     2513        }(this, moxie));
    22972514
    2298         return PluploadFile;
    2299 }());
    2300 
    2301 
    2302 /**
    2303  * Constructs a queue progress.
    2304  *
    2305  * @class QueueProgress
    2306  * @constructor
    2307  */
    2308  plupload.QueueProgress = function() {
    2309         var self = this; // Setup alias for self to reduce code size when it's compressed
    2310 
    2311         /**
    2312          * Total queue file size.
    2313          *
    2314          * @property size
    2315          * @type Number
    2316          */
    2317         self.size = 0;
    2318 
    2319         /**
    2320          * Total bytes uploaded.
    2321          *
    2322          * @property loaded
    2323          * @type Number
    2324          */
    2325         self.loaded = 0;
    2326 
    2327         /**
    2328          * Number of files uploaded.
    2329          *
    2330          * @property uploaded
    2331          * @type Number
    2332          */
    2333         self.uploaded = 0;
    2334 
    2335         /**
    2336          * Number of files failed to upload.
    2337          *
    2338          * @property failed
    2339          * @type Number
    2340          */
    2341         self.failed = 0;
    2342 
    2343         /**
    2344          * Number of files yet to be uploaded.
    2345          *
    2346          * @property queued
    2347          * @type Number
    2348          */
    2349         self.queued = 0;
    2350 
    2351         /**
    2352          * Total percent of the uploaded bytes.
    2353          *
    2354          * @property percent
    2355          * @type Number
    2356          */
    2357         self.percent = 0;
    2358 
    2359         /**
    2360          * Bytes uploaded per second.
    2361          *
    2362          * @property bytesPerSec
    2363          * @type Number
    2364          */
    2365         self.bytesPerSec = 0;
    2366 
    2367         /**
    2368          * Resets the progress to its initial values.
    2369          *
    2370          * @method reset
    2371          */
    2372         self.reset = function() {
    2373                 self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
    2374         };
    2375 };
    2376 
    2377 window.plupload = plupload;
    2378 
    2379 }(window, mOxie));
     2515}));
     2516 No newline at end of file
  • src/wp-includes/script-loader.php

     
    12511251                'error_uploading'           => __( '&#8220;%s&#8221; has failed to upload.' ),
    12521252        );
    12531253
    1254         $scripts->add( 'moxiejs', "/wp-includes/js/plupload/moxie$suffix.js", array(), '1.3.5' );
    1255         $scripts->add( 'plupload', "/wp-includes/js/plupload/plupload$suffix.js", array( 'moxiejs' ), '2.1.9' );
     1254        $scripts->add( 'moxiejs', "/wp-includes/js/plupload/moxie$suffix.js", array(), '1.5.7' );
     1255        $scripts->add( 'plupload', "/wp-includes/js/plupload/plupload$suffix.js", array( 'moxiejs' ), '2.3.6' );
    12561256        // Back compat handles:
    12571257        foreach ( array( 'all', 'html5', 'flash', 'silverlight', 'html4' ) as $handle ) {
    12581258                $scripts->add( "plupload-$handle", false, array( 'plupload' ), '2.1.1' );